private void translateInterfaceFuncDef(
      InterfaceDef interfaceDef, List<ClassDef> instances, FuncDef funcDef) {
    AstElement trace = funcDef;
    ImFunction f = translator.getDynamicDispatchFuncFor(funcDef);
    Map<ClassDef, FuncDef> instances2 = translator.getClassesWithImplementation(instances, funcDef);
    if (instances2.size() > 0) {
      int maxTypeId = translator.getMaxTypeId(instances);
      f.getBody()
          .addAll(
              translator.createDispatch(instances2, funcDef, f, maxTypeId, new TypeIdGetterImpl()));
    }
    if (!funcDef.attrHasEmptyBody()) {
      // TODO add default implementation
      f.getBody().addAll(translator.translateStatements(f, funcDef.getBody()));
    } else {
      // create dynamic message when not matched:
      String msg =
          "ERROR: invalid type for interface dispatch when calling "
              + interfaceDef.getName()
              + "."
              + funcDef.getName();

      f.getBody()
          .add(
              JassIm.ImFunctionCall(
                  trace, translator.getDebugPrintFunc(), ImExprs(ImStringVal(msg))));
      if (!(funcDef.attrTyp() instanceof WurstTypeVoid)) {
        // add return statement
        ImType type = f.getReturnType();
        ImExpr def = translator.getDefaultValueForJassType(type);
        f.getBody().add(JassIm.ImReturn(trace, def));
      }
    }
  }
  private void rewriteFuncRefs(final List<ImFuncRef> funcRefs, Set<ImFunction> affectedFuncs) {
    // rewrite funcrefs
    for (ImFuncRef fr : funcRefs) {
      ImFunction f = fr.getFunc();
      if (!affectedFuncs.contains(f)) {
        continue;
      }

      ImFunction bridgeFunc =
          JassIm.ImFunction(
              f.getTrace(),
              "bridge_" + f.getName(),
              f.getParameters().copy(),
              (ImType) f.getReturnType().copy(),
              JassIm.ImVars(),
              JassIm.ImStmts(),
              f.getFlags());
      prog.getFunctions().add(bridgeFunc);

      // remove statcktrace var from params
      bridgeFunc.getParameters().remove(getStackTraceVar(bridgeFunc));

      ImStmt stmt;
      ImExprs args = JassIm.ImExprs(str("\n   " + fr.attrTrace().attrSource().printShort()));
      ImFunctionCall call = JassIm.ImFunctionCall(fr.attrTrace(), f, args, true, CallType.NORMAL);
      if (bridgeFunc.getReturnType() instanceof ImVoid) {
        stmt = call;
      } else {
        stmt = JassIm.ImReturn(fr.attrTrace(), call);
      }
      bridgeFunc.getBody().add(stmt);

      fr.setFunc(bridgeFunc);
    }
  }
  private void translateInterfaceFuncDef(FuncDef f) {
    ImMethod imMeth = translator.getMethodFor(f);
    ImFunction imFunc = translator.getFuncFor(f);
    imClass.getMethods().add(imMeth);

    // translate implementation
    if (f.attrHasEmptyBody()) {
      imMeth.setIsAbstract(true);
    } else {
      // there is a default implementation
      imFunc.getBody().addAll(translator.translateStatements(imFunc, f.getBody()));
    }

    List<ClassDef> subClasses = Lists.newArrayList(translator.getInterfaceInstances(interfaceDef));
    // TODO also add extended interfaces

    // set sub methods
    Map<ClassDef, FuncDef> subClasses2 = translator.getClassesWithImplementation(subClasses, f);
    for (Entry<ClassDef, FuncDef> subE : subClasses2.entrySet()) {
      ClassDef subC = subE.getKey();
      ImmutableCollection<WurstTypeInterface> interfaces = subC.attrImplementedInterfaces();

      Map<TypeParamDef, WurstTypeBoundTypeParam> typeBinding =
          interfaces
              .stream()
              .filter(t -> t.getDef() == interfaceDef)
              .map(t -> t.getTypeArgBinding())
              .findFirst()
              .orElse(Collections.emptyMap());

      FuncDef subM = subE.getValue();
      ImMethod m = translator.getMethodFor(subM);

      ImClass mClass = translator.getClassFor(subC);
      OverrideUtils.addOverride(translator, f, mClass, m, subM, typeBinding);
    }
  }