diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index ba1633526..78aaa0ba1 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -213,11 +213,9 @@ compileJava.dependsOn gen test { // set minimal heap size required to run tests: - jvmArgs = ['-Xms256m'] + maxHeapSize = "1G" - useTestNG() { - suites 'src/test/resources/AllTestsSuite.xml' - } + useTestNG() } // delete the generated sources on clean diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index 006a6cf62..d4d2b1de4 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -9,15 +9,14 @@ ImProg( ImFunctions functions, ImMethods methods, ImClasses classes, - ImTypeClassFuncs typeClassFunctions, java.util.Map> globalInits) ImVars * ImVar ImFunctions * ImFunction ImClasses * ImClass -ImTypeClassFuncs * ImTypeClassFunc -ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, boolean isBJ) +ImVar(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImType type, String name, + java.util.List varFlags) ImType = ImSimpleType(String typename) @@ -37,30 +36,28 @@ ImTypeVars * ImTypeVar ImTypeVar(String name) -ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, +ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, String name, ImTypeVars typeVariables, - ImVars parameters, + ImVars parameters, ref ImType returnType, ImVars locals, ImStmts body, java.util.List flags) -ImTypeClassFunc(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, - String name, - ImTypeVars typeVariables, - ImVars parameters, - ref ImType returnType) ImClass(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, String name, ImTypeVars typeVariables, - ImVars fields, + ImVars fields, ImMethods methods, ImFunctions functions, java.util.List superClasses) + +ImElementWithTypeVars = ImFunction | ImClass + ImMethods * ImMethod ImMethod(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, @@ -100,10 +97,10 @@ ImExpr = | ImGetStackTrace() | ImCompiletimeExpr(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr expr, int executionOrderIndex) | ImLExpr - | ImTypeVarDispatch(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImTypeClassFunc typeClassFunc, ImExprs arguments - , ref ImTypeVar typeVariable) | ImCast(ImExpr expr, ref ImType toType) + + // an expression which can be used on the left hand side of an assignment ImLExpr = ImVarAccess(ref ImVar var) @@ -129,7 +126,7 @@ ImClassRelatedExprWithClass = | ImInstanceof(ImExpr obj, ref ImClassType clazz) | ImTypeIdOfObj(ImExpr obj, ref ImClassType clazz) | ImTypeIdOfClass(ref ImClassType clazz) - + | ImTypeClassDictValue(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImClassType clazz, ImExprs arguments) @@ -148,7 +145,7 @@ ImConst = ImTypeArguments * ImTypeArgument -ImTypeArgument(ref ImType type, java.util.Map> typeClassBinding) +ImTypeArgument(ref ImType type) // helper types: @@ -380,3 +377,6 @@ ImLExpr.isUsedAsLValue() returns boolean implemented by de.peeeq.wurstscript.translation.imtranslation.LValues.isUsedAsLValue +ImVar.isBJ() + returns boolean + implemented by de.peeeq.wurstscript.translation.imtranslation.VarFlag.isBj \ No newline at end of file diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index 2aaab52eb..765f6364e 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -36,6 +36,7 @@ WEntity = | ModuleDef(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId, TypeParamDefs typeParameters, ClassDefs innerClasses, FuncDefs methods, GlobalVarDefs vars, ConstructorDefs constructors, ModuleInstanciations p_moduleInstanciations, ModuleUses moduleUses, OnDestroyDef onDestroy) + | InstanceDecl(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, TypeParamDefs typeParameters, TypeExpr implementedInterface, FuncDefs methods) @@ -117,7 +118,12 @@ TypeParamDefs * TypeParamDef TypeParamDef(@ignoreForEquality de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId, TypeParamConstraints typeParamConstraints) -TypeParamConstraints = NoTypeParamConstraints() | TypeExprList +TypeParamConstraints = NoTypeParamConstraints() | TypeParamConstraintList + +TypeParamConstraintList * TypeParamConstraint + +TypeParamConstraint(TypeExpr constraint) + WParameters * WParameter @@ -282,6 +288,7 @@ WScope = | WBlock | WEntities | ExprClosure + | InstanceDecl PackageOrGlobal = WPackage | CompilationUnit @@ -310,7 +317,7 @@ Modifier = // ElementWithBody = FunctionImplementation | InitBlock | ConstructorDef | OnDestroyDef //ElementWithModifier = NameDef | TypeDef | ModuleDef | ConstructorDef | GlobalVarDef | FunctionDefinition -HasModifier = NameDef | TypeDef | ModuleDef | ConstructorDef | GlobalVarDef | FunctionDefinition +HasModifier = NameDef | TypeDef | ModuleDef | ConstructorDef | GlobalVarDef | FunctionDefinition | InstanceDecl HasTypeArgs = ExprNewObject | FunctionCall | ModuleUse | StmtCall | TypeExprSimple AstElementWithFuncName = ExprFunctionCall | ExprMemberMethod | ExprFuncRefc @@ -323,7 +330,7 @@ AstElementWithNameId = WPackage | NativeFunc | ModuleDef | TypeDef | ModuleInsta AstElementWithParameters = FunctionDefinition | ExtensionFuncDef | NativeFunc | TupleDef | ConstructorDef | FuncDef -AstElementWithTypeParameters = ExtensionFuncDef | ModuleDef | ClassDef | InterfaceDef | FuncDef +AstElementWithTypeParameters = ExtensionFuncDef | ModuleDef | ClassOrInterfaceOrInstance | FuncDef AstElementWithArgs = StmtCall | ExprFunctionCall | ExprNewObject | ExprMemberMethod @@ -350,6 +357,8 @@ StructureDef = ClassOrModuleOrModuleInstanciation | ClassOrInterface ClassOrInterface = ClassDef | InterfaceDef +ClassOrInterfaceOrInstance = ClassOrInterface | InstanceDecl + ClassOrModuleInstanciation = ClassDef | ModuleInstanciation ClassOrModuleOrModuleInstanciation = ClassOrModule | ClassOrModuleInstanciation @@ -506,6 +515,14 @@ Element.attrNearestScope() returns @Nullable WScope implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestScope +TypeParamConstraint.parentTypeParam() + returns TypeParamDef + implemented by de.peeeq.wurstscript.attributes.AttrNearest.parentTypeParam + +TypeParamConstraint.attrConstraintTyp() + returns de.peeeq.wurstscript.types.WurstType + implemented by de.peeeq.wurstscript.attributes.AttrTypeExprType.constraintType + WScope.attrNextScope "returns the scope surrounding this scope" returns @Nullable WScope implemented by de.peeeq.wurstscript.attributes.AttrNearest.nextScope @@ -527,6 +544,10 @@ Element.attrNearestClassOrInterface() returns @Nullable ClassOrInterface implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassOrInterface +Element.attrNearestClassOrInterfaceOrInstance() + returns @Nullable ClassOrInterfaceOrInstance + implemented by de.peeeq.wurstscript.attributes.AttrNearest.attrNearestClassOrInterfaceOrInstance + Element.attrNearestClassOrModule() returns @Nullable ClassOrModule implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassOrModule @@ -893,6 +914,13 @@ WPackage.attrExportedTypeNameLinks returns com.google.common.collect.ImmutableMultimap implemented by de.peeeq.wurstscript.attributes.names.Exports.exportedTypeNameLinks +WPackage.attrTypeClasses + returns java.util.List + implemented by de.peeeq.wurstscript.TypeClasses.availableTypeClasses + +WPackage.attrDefinedTypeClasses + returns java.util.List + implemented by de.peeeq.wurstscript.TypeClasses.definedTypeClasses Element.lookupType(String name, boolean showErrors) returns @Nullable TypeDef diff --git a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 index 8bc9e740d..4dfc9f987 100644 --- a/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 +++ b/de.peeeq.wurstscript/src/main/antlr/de/peeeq/wurstscript/antlr/Wurst.g4 @@ -1,3 +1,4 @@ + grammar Wurst; @header { @@ -112,8 +113,16 @@ entity: | interfaceDef | tupleDef | extensionFuncDef + | instanceDeclaration ; +instanceDeclaration: + modifiersWithDoc 'implements' implemented=typeExpr ('for' (params+=typeParam (',' params+=typeParam)*))? + NL (STARTBLOCK + funcDef* + ENDBLOCK)? + ; + interfaceDef: modifiersWithDoc 'interface' name=ID typeParams ('extends' extended+=typeExpr (',' extended+=typeExpr)*)? diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java index 2680e6f62..d1be15aee 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java @@ -241,7 +241,7 @@ private void executeCompiletimeExpr(ImCompiletimeExpr cte) { public ImVar initFor(ILconstObject obj) { - ImVar res = JassIm.ImVar(obj.getTrace(), obj.getType(), obj.getType() + "_compiletime", false); + ImVar res = JassIm.ImVar(obj.getTrace(), obj.getType(), obj.getType() + "_compiletime", Collections.emptyList()); imProg.getGlobals().add(res); ImAlloc alloc = JassIm.ImAlloc(obj.getTrace(), obj.getType()); addCompiletimeStateInitAlloc(alloc.getTrace(), res, alloc); @@ -281,7 +281,7 @@ public ImVar initFor(IlConstHandle a) { @SuppressWarnings("unchecked") ArrayListMultimap map = (ArrayListMultimap) obj; ImType type = TypesHelper.imHashTable(); - ImVar res = JassIm.ImVar(trace, type, type + "_compiletime", false); + ImVar res = JassIm.ImVar(trace, type, type + "_compiletime", Collections.emptyList()); imProg.getGlobals().add(res); init = constantToExprHashtable(trace, res, a, map); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java index b1d8a14a3..0e6c4ffe5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java @@ -346,7 +346,7 @@ private void resolveImport(Function addCompilationUnit, S private CompilationUnit loadLibPackage(Function addCompilationUnit, String imp) { File file = getLibs().get(imp); if (file == null) { - gui.sendError(new CompileError(new WPos("", null, 0, 0), "Could not find lib-package " + imp + ". Are you missing your wurst.dependencies file?")); + gui.sendError(new CompileError(WPos.noSource(), "Could not find lib-package " + imp + ". Are you missing your wurst.dependencies file?")); return Ast.CompilationUnit(new CompilationUnitInfo(errorHandler), Ast.JassToplevelDeclarations(), Ast.WPackages()); } else { return addCompilationUnit.apply(file); @@ -410,10 +410,22 @@ public JassProg transformProgToJass() { beginPhase(2, "Eliminate generics"); new EliminateGenerics(imTranslator2, imProg2).transform(); printDebugImProg("./test-output/im " + stage++ + "_genericsEliminated.im"); + if (!runArgs.isLua()) { + try { + EliminateTypeClasses.transform(imTranslator2); + } finally { + printDebugImProg("./test-output/at_crash.im"); + } + } + printDebugImProg("./test-output/im " + stage++ + "_typeClassesEliminated.im"); + // eliminate classes beginPhase(2, "translate classes"); + ClassesOptimizer.optimizeProg(imTranslator2); + printDebugImProg("./test-output/im " + stage++ + "_classes_optimized.im"); + new EliminateClasses(imTranslator2, imProg2, !runArgs.isUncheckedDispatch()).eliminateClasses(); imTranslator2.assertProperties(); printDebugImProg("./test-output/im " + stage++ + "_classesEliminated.im"); @@ -543,7 +555,7 @@ private void addJassHotCodeReloadCode() { ImFunction jhcr_reload = JassIm.ImFunction(trace, "jhcr_reload_on_escape", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), reloadBody, Collections.emptyList()); - ImVar trig = JassIm.ImVar(trace, TypesHelper.imTrigger(), "trig", false); + ImVar trig = JassIm.ImVar(trace, TypesHelper.imTrigger(), "trig", Collections.emptyList()); mainFunc.getLocals().add(trig); // TriggerRegisterPlayerEventEndCinematic(trig, Player(0)) stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(trig), callExtern(trace, CallType.NORMAL, "CreateTrigger"))); @@ -611,6 +623,8 @@ private void printDebugImProg(String debugFile) { } catch (IOException e) { ErrorReporting.instance.handleSevere(e, getCompleteSourcecode()); } + // basic sanity check + getImTranslator().assertProperties(AssertProperty.rooted(getImProg())); } private WurstModel mergeCompilationUnits(List compilationUnits) { @@ -817,4 +831,12 @@ public LuaCompilationUnit transformProgToLua() { LuaCompilationUnit luaCode = luaTranslator.translate(); return luaCode; } + + /** + * Clears the intermediate output programs to allow collection by garbage collector + */ + public void clear() { + imProg = null; + prog = null; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java index 92b5e89e0..3b8910ff8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java @@ -118,6 +118,11 @@ public void case_TupleDef(TupleDef tupleDef) { add(tupleDef.getName(), SymbolKind.Class); } + @Override + public void case_InstanceDecl(InstanceDecl instanceDecl) { + // ignore + } + @Override public void case_FuncDef(FuncDef funcDef) { SymbolKind kind = funcDef.attrIsDynamicClassMember() ? SymbolKind.Method : SymbolKind.Function; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java index 67fb6b941..9f80d3a23 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/HoverInfo.java @@ -631,6 +631,11 @@ public List> case_ExprNull(ExprNull exprNull) { return string("The null-reference"); } + @Override + public List> case_TypeParamConstraintList(TypeParamConstraintList typeParamConstraintList) { + return string("Type parameter constraints define type classes that must be implemented for type parameters."); + } + @Override public List> case_ClassDefs(ClassDefs classDefs) { return string("A list of class definitions."); @@ -736,6 +741,11 @@ public List> case_ExprIfElse(ExprIfElse exprIfElse) return string("A conditional expression (condition ? ifTrue : ifFalse)."); } + @Override + public List> case_TypeParamConstraint(TypeParamConstraint t) { + return string(t.description()); + } + @Override public List> case_WurstDoc(WurstDoc wurstDoc) { return wurstDoc.getParent().match(this); @@ -766,6 +776,11 @@ public List> case_ModVararg(ModVararg modVararg) { return string("Declares the parameter to be a array of variable length"); } + @Override + public List> case_InstanceDecl(InstanceDecl instanceDecl) { + return string("An instance declaration for the type-class " + instanceDecl.getImplementedInterface().attrTyp()); + } + @Override public List> case_TypeExprResolved(TypeExprResolved typeExprResolved) { return typeExpr(typeExprResolved); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java index 33e69de75..be72e2f2d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/SymbolInformationRequest.java @@ -111,6 +111,11 @@ public void case_TupleDef(TupleDef tupleDef) { add(tupleDef.getName(), SymbolKind.Class); } + @Override + public void case_InstanceDecl(InstanceDecl instanceDecl) { + // ignore + } + @Override public void case_FuncDef(FuncDef funcDef) { SymbolKind kind = funcDef.attrIsDynamicClassMember() ? SymbolKind.Method : SymbolKind.Function; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java new file mode 100644 index 000000000..65f321b9b --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/TypeClasses.java @@ -0,0 +1,280 @@ +package de.peeeq.wurstscript; + +import com.google.common.collect.ImmutableMultimap; +import de.peeeq.immutablecollections.ImmutableList; +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.names.DefLink; +import de.peeeq.wurstscript.attributes.names.FuncLink; +import de.peeeq.wurstscript.types.*; +import de.peeeq.wurstscript.utils.Lazy; +import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; +import io.vavr.Tuple2; +import io.vavr.collection.HashMap; +import io.vavr.control.Option; +import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TypeClasses { + /** + * How often an instance declaration can be used in a single derivation. + */ + private static final int DERIVATION_MAX_INSTANCE_USES = 10; + + private static final ImmutableList OBJ_INSTANCES = ImmutableList.of("FromIndex", "ToIndex", "ConvertIndex", "Default", "AnyRef"); + + public static Pair> findTypeClasses(FunctionSignature sig, StmtCall fc) { + List errors = new ArrayList<>(); + VariableBinding mapping = sig.getMapping(); + for (TypeParamDef tp : sig.getDefinitionTypeVariables()) { + Option matchedTypeOpt = mapping.get(tp); + List constraints = getConstraints(tp); + if (matchedTypeOpt.isEmpty()) { + if (!constraints.isEmpty()) { + errors.add(new CompileError(fc.attrSource(), "Type parameter " + tp.getName() + " is not bound, so type constraints cannot be solved.")); + } + continue; + } + WurstTypeBoundTypeParam matchedType = matchedTypeOpt.get(); + for (WurstTypeInterface constraint : constraints) { + VariableBinding mapping2 = findTypeClass(fc, errors, mapping, tp, matchedType, constraint); + if (mapping2 != null) { + mapping = mapping2; + } + } + } + sig = sig.setTypeArgs(fc, mapping); + return Pair.create(sig, errors); + } + + @NotNull + public static List getConstraints(TypeParamDef tp) { + List constraints = new ArrayList<>(); + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + for (TypeParamConstraint c : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { + WurstType ct = c.attrConstraintTyp(); + if (ct instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) ct; + constraints.add(wti); + + + } + } + } + return constraints; + } + + /** + * Finds an instance of a type class + * + * @param location + * @param errors + * @param mapping + * @param tp + * @param matchedType + * @param constraint + * @return the updated type var mapping if any was found + *

+ * TODO change to stream return type to allow backtracking + */ + public static VariableBinding findTypeClass(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint) { + return findTypeClassH(location, errors, mapping, tp, matchedType, constraint, HashMap.empty()); + } + + private static VariableBinding findTypeClassH(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint1, HashMap uses) { + WurstTypeInterface constraint = (WurstTypeInterface) constraint1.setTypeArgs(mapping); + + // option 1: the matched type is a type param that also has the right constraint: + VariableBinding mapping2 = findDerived(location, matchedType, constraint, tp, mapping); + if (mapping2 != null) { + return mapping2; + } + // option 2: find instance declarations + mapping2 = findInstanceDeclarations(location, errors, mapping, tp, matchedType, uses, constraint); + if (mapping2 != null) { + return mapping2; + } + // option 3: built-in instance for objects + if (OBJ_INSTANCES.contains(constraint.getDef().getName())) { + if (matchedType.getBaseType() instanceof WurstTypeClassOrInterface) { + WurstTypeClassOrInterface objectType = (WurstTypeClassOrInterface) matchedType.getBaseType(); + + TypeClassInstance instance = TypeClassInstance.fromObject(objectType, constraint.getDef()); + return mapping.withTypeClassInstance(tp, matchedType, instance); + } + } + // not found: + errors.add(new CompileError(location, + "Type " + matchedType + " does not satisfy constraint " + tp.getName() + ": " + constraint.getName())); + return null; + } + + private static VariableBinding findDerived(Element location, WurstTypeBoundTypeParam matchedType, WurstTypeInterface constraint, TypeParamDef tp, VariableBinding mapping) { + if (matchedType.getBaseType() instanceof WurstTypeTypeParam) { + WurstTypeTypeParam wtp = (WurstTypeTypeParam) matchedType.getBaseType(); + Optional matchingConstraint = wtp.getTypeConstraints().stream() + .filter(c -> c.attrConstraintTyp().isSubtypeOf(constraint, location)).findFirst(); + if (matchingConstraint.isPresent()) { + TypeClassInstance instance = TypeClassInstance.fromTypeParam( + location, matchingConstraint.get()); + return mapping.withTypeClassInstance(tp, matchedType, instance); + } + } + return null; + } + + + @org.jetbrains.annotations.Nullable + private static VariableBinding findInstanceDeclarations(Element location, List errors, VariableBinding mapping, TypeParamDef tp, WurstTypeBoundTypeParam matchedType, HashMap uses, WurstTypeInterface constraint) { + // TODO create index to make this faster and use normal scoped lookup (ony search imports) + + @Nullable PackageOrGlobal wPackageG = location.attrNearestPackage(); + if (!(wPackageG instanceof WPackage)) { + return null; + } + WPackage wPackage = (WPackage) wPackageG; + List instances = wPackage.attrTypeClasses().stream() + .flatMap(instance -> { + int instanceUses = uses.getOrElse(instance, 0); + if (instanceUses > 10) { + return Stream.empty(); + } + Lazy> uses2 = Lazy.create(() -> uses.put(instance, instanceUses + 1)); + + WurstType instanceType; + try { + instanceType = instance.getImplementedInterface().attrTyp(); + } catch (CyclicDependencyError e) { + return Stream.empty(); + } + // TODO instead of initialMapping should use previous mapping here + // but that would require fresh variables (needs refactoring) + VariableBinding initialMapping = VariableBinding.emptyMapping().withTypeVariables(instance.getTypeParameters()); + VariableBinding match = instanceType.matchAgainstSupertype(constraint, location, initialMapping, VariablePosition.LEFT); + + if (match == null) { + return Stream.empty(); + } + instanceType = instanceType.setTypeArgs(match); + + + for (Tuple2 m : match) { + TypeParamDef instanceTp = m._1(); + WurstTypeBoundTypeParam mType = m._2(); + List instanceConstraints = getConstraints(instanceTp); + for (WurstTypeInterface instanceConstraint : instanceConstraints) { + VariableBinding match2 = findTypeClassH(location, errors, match, instanceTp, mType, instanceConstraint, uses2.get()); + if (match2 == null) { + return Stream.empty(); + } + match = match2; + } + } + + List deps = new ArrayList<>(); + List typeArgs = new ArrayList<>(); + for (TypeParamDef instanceTp : instance.getTypeParameters()) { + WurstTypeBoundTypeParam i = match.get(instanceTp).get(); + @Nullable List is = i.getInstances(); + if (is != null) { + deps.addAll(is); + } + if (instanceTp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + typeArgs.add(i); + } + } + + + // TODO resolve dependencies + + TypeClassInstance result = TypeClassInstance.fromInstance(instance, typeArgs, deps, (WurstTypeInterface) instanceType); + + return Stream.of(result); + + }).collect(Collectors.toList()); + + if (instances.isEmpty()) { + return null; + } else { + if (instances.size() > 1) { + errors.add(new CompileError(location, + "There are multiple instances for type " + matchedType + " and constraint " + tp.getName() + ": " + constraint.getName() + "\n" + + Utils.printSep("\n", instances))); + + } + TypeClassInstance instance = Utils.getFirst(instances); + return mapping.withTypeClassInstance(tp, matchedType, instance); + } + } + + public static List availableTypeClasses(WPackage p) { + List result = new ArrayList<>(p.attrDefinedTypeClasses()); + Set visited = new HashSet<>(); + for (WImport imp : p.getImports()) { + @Nullable WPackage impP = imp.attrImportedPackage(); + collectImportedInstances(result, visited, impP); + } + return result; + } + + private static void collectImportedInstances(List result, Set visited, WPackage p) { + if (p == null) { + return; + } + if (visited.contains(p)) { + return; + } + visited.add(p); + for (InstanceDecl inst : p.attrDefinedTypeClasses()) { + if (inst.attrIsPublic()) { + result.add(inst); + } + } + for (WImport imp : p.getImports()) { + if (imp.getIsPublic()) { + @Nullable WPackage impP = imp.attrImportedPackage(); + collectImportedInstances(result, visited, impP); + } + } + } + + public static List definedTypeClasses(WPackage p) { + return p.getElements().stream() + .filter(e -> e instanceof InstanceDecl) + .map(e -> (InstanceDecl) e) + .collect(Collectors.toList()); + } + + public static void checkInstance(InstanceDecl c) { + ImmutableMultimap nameLinks = c.attrNameLinks(); + if (!c.attrIsAbstract()) { + StringBuilder toImplement = new StringBuilder(); + // should have no abstract methods + for (DefLink link : nameLinks.values()) { + NameDef f = link.getDef(); + if (f.attrIsAbstract()) { + if (f.attrNearestStructureDef() == c) { + Element loc = f.getModifiers().stream() + .filter(m -> m instanceof ModAbstract) + .map(x -> x) + .findFirst() + .orElse(f); + loc.addError(Utils.printElementWithSource(c) + " cannot have abstract functions like " + f.getName()); + } else if (link instanceof FuncLink) { + toImplement.append("\n "); + toImplement.append(((FuncLink) link).printFunctionTemplate()); + } + } + } + if (toImplement.length() > 0) { + c.addError(Utils.printElementWithSource(c) + " must implement the following functions:" + toImplement); + } + } + + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java index 02d865e41..0f2c80873 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrCallSignature.java @@ -17,12 +17,6 @@ public static CallSignature calculate(ExprFunctionCall c) { } public static CallSignature calculate(ExprMemberMethod c) { - Expr receiver = c.getLeft(); - if (receiver.attrTyp().isStaticRef()) { - // if we use a static ref like A.foo() - // then there is no real receiver - receiver = null; - } return new CallSignature(c.attrImplicitParameter(), c.getArgs()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java index 0c03fe752..65f425687 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java @@ -6,6 +6,7 @@ import de.peeeq.wurstscript.WurstOperator; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; +import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.names.Visibility; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; @@ -324,10 +325,26 @@ private static List useLocalPackageIfPossible(FuncRef node, return Utils.getFirst(funcs); } + // if still ambiguous, remove methods with type classes to be resolved + funcs = filterMethodsWithTypeClasses(node, funcs); + if (funcs.size() == 1) { + return Utils.getFirst(funcs); + } + node.addError("Call to function " + funcName + " is ambiguous. Alternatives are:\n" + Utils.printAlternatives(funcs)); return Utils.getFirst(funcs); } + private static List filterMethodsWithTypeClasses(Expr node, List funcs) { + List funcs2 = funcs.stream() + .filter(n -> !n.hasTypeClassConstraints()) + .collect(Collectors.toList()); + if (funcs2.isEmpty()) { + return funcs; + } + return funcs2; + } + private static List filterByParameters(Element node, List argumentTypes, List funcs) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java index 3d72653d0..2345dddec 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java @@ -1,11 +1,11 @@ package de.peeeq.wurstscript.attributes; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; -import de.peeeq.wurstscript.types.FunctionSignature; -import de.peeeq.wurstscript.types.VariableBinding; -import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeUnknown; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; +import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; +import io.vavr.Tuple2; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -47,6 +47,7 @@ private static FunctionSignature filterSigs( return candidates.get(0); } + sigs = candidates; candidates = filterByIfNotDefinedAnnotation(candidates); if (candidates.isEmpty()) { // parameters match for no element, just return the first signature @@ -55,7 +56,14 @@ private static FunctionSignature filterSigs( return candidates.get(0); } - + sigs = candidates; + candidates = filterByHasTypeClasses(candidates); + if (candidates.isEmpty()) { + // parameters match for no element, just return the first signature + return Utils.getFirst(sigs); + } else if (candidates.size() == 1) { + return candidates.get(0); + } if (argTypes.stream().noneMatch(t -> t instanceof WurstTypeUnknown)) { @@ -70,6 +78,16 @@ private static FunctionSignature filterSigs( return candidates.get(0); } + private static List filterByHasTypeClasses(List candidates) { + List res = candidates.stream() + .filter(sig -> !sig.hasTypeClassConstraints()) + .collect(Collectors.toList()); + if (res.isEmpty()) { + return candidates; + } + return res; + } + private static List filterByIfNotDefinedAnnotation(List candidates) { return candidates.stream() .filter(sig -> !sig.hasIfNotDefinedAnnotation()) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java index 9402be839..839e6907e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.types.WurstTypeTypeParam; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -78,6 +79,11 @@ static OptExpr getFunctionCallImplicitParameter(FunctionCall e, FuncLink calledF if (res != null) { return res; } + if (hasReceiver.getLeft().attrTyp() instanceof WurstTypeTypeParam) { + // for type parameters we have no implicit parameter here, + // (one is added later in the translation when constraints are resolved) + return Ast.NoExpr(); + } } if (calledFunc == null) { return Ast.NoExpr(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java index 5359a9a4c..33df1db28 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrIsClassMember.java @@ -26,7 +26,7 @@ public static boolean calculate(FuncDef f) { if (f.attrNearestNamedScope() instanceof StructureDef) { return !f.attrIsStatic(); } - return false; + return f.attrNearestClassOrInterfaceOrInstance() instanceof InstanceDecl; } public static boolean calculate(ExtensionFuncDef f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java index 517585082..9c5c46481 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNearest.java @@ -159,4 +159,17 @@ public static ClassOrInterface nearestClassOrInterface(Element node) { } + public static TypeParamDef parentTypeParam(TypeParamConstraint tp) { + return (TypeParamDef) tp.getParent().getParent(); + } + + public static ClassOrInterfaceOrInstance attrNearestClassOrInterfaceOrInstance(Element e) { + while (e != null) { + if (e instanceof ClassOrInterfaceOrInstance) { + return (ClassOrInterfaceOrInstance) e; + } + e = e.getParent(); + } + return null; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java index 73c95c7d7..dd739eabe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPos.java @@ -31,7 +31,7 @@ public static WPos getPos(Element e) { min = Math.min(min, childSource.getLeftPos()); max = Math.max(max, childSource.getRightPos()); } - return new WPos(e.get(0).attrSource().getFile(), e.get(0).attrSource().getLineOffsets(), min, max); + return new WPos(e.get(0).attrSource().getFileInfo(), min, max); } // if no childs exist, search a parent element with a explicit position return getParentSource(e); @@ -49,7 +49,7 @@ public static WPos getPos(WImports e) { max = Math.max(max, childSource.getRightPos()); } if (min != Integer.MAX_VALUE) { - return new WPos(e.get(0).attrSource().getFile(), e.get(0).attrSource().getLineOffsets(), min, max); + return new WPos(e.get(0).attrSource().getFileInfo(), min, max); } else { return getParentSource(e); } @@ -62,7 +62,7 @@ private static WPos getParentSource(Element e) { if (parent instanceof AstElementWithSource) { WPos parentSource = ((AstElementWithSource) parent).getSource(); // use parent position but with size -1, so we do not go into this - return new WPos(parentSource.getFile(), parentSource.getLineOffsets(), parentSource.getLeftPos(), parentSource.getLeftPos() - 1); + return new WPos(parentSource.getFileInfo(), parentSource.getLeftPos(), parentSource.getLeftPos() - 1); } parent = parent.getParent(); } @@ -85,6 +85,10 @@ public static WPos getErrorPos(Element e) { return e.attrSource(); } + public static WPos getErrorPos(InstanceDecl e) { + return e.getImplementedInterface().attrErrorPos(); + } + public static WPos getErrorPos(SomeSuperConstructorCall e) { return e.getKeywordSource(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java index 74df1dc5f..968d9badd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java @@ -3,14 +3,16 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult; +import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; import static de.peeeq.wurstscript.attributes.GenericsHelper.givenBinding; import static de.peeeq.wurstscript.attributes.GenericsHelper.typeParameters; @@ -45,35 +47,56 @@ public static ImmutableCollection calculate(FunctionCall fc) private static ImmutableCollection findBestSignature(StmtCall fc, ImmutableCollection res) { ImmutableCollection.Builder resultBuilder2 = ImmutableList.builder(); + ImmutableCollection.Builder>> typeClassErrors = ImmutableList.builder(); List argTypes = AttrFuncDef.argumentTypesPre(fc); for (FunctionSignature sig : res) { FunctionSignature sig2 = sig.matchAgainstArgs(argTypes, fc); - if (sig2 != null) { - resultBuilder2.add(sig2); + if (sig2 == null) { + continue; + } + Pair> typeClassMatched = TypeClasses.findTypeClasses(sig2, fc); + if (typeClassMatched.getB().isEmpty()) { + resultBuilder2.add(typeClassMatched.getA()); + } else { + typeClassErrors.add(typeClassMatched); } } ImmutableCollection res2 = resultBuilder2.build(); - if (res2.isEmpty()) { - // no signature matches precisely --> try to match as good as possible - ImmutableList match3 = res.stream() - .map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc)) - .collect(ImmutableList.toImmutableList()); - - if (match3.isEmpty()) { - return ImmutableList.of(); - } else { - // add errors from best match (minimal badness) - ArgsMatchResult min = Collections.min(match3, Comparator.comparing(ArgsMatchResult::getBadness)); - for (CompileError c : min.getErrors()) { - fc.getErrorHandler().sendError(c); - } + if (!res2.isEmpty()) { + return res2; + } + // if no precise matches, check if there are any matches that just miss some type class constraints: + ImmutableCollection>> typeClassErrorsL = typeClassErrors.build(); + if (!typeClassErrorsL.isEmpty()) { + CompileError err = typeClassErrorsL.stream() + .flatMap(x -> x.getB().stream()) + .findFirst() + .get(); + + fc.getErrorHandler().sendError(err); + + return typeClassErrorsL.stream() + .map(Pair::getA) + .collect(ImmutableList.toImmutableList()); + } - return match3.stream() - .map(ArgsMatchResult::getSig) - .collect(ImmutableList.toImmutableList()); - } + // no signature matches precisely --> try to match as good as possible + ImmutableList match3 = res.stream() + .map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc)) + .collect(ImmutableList.toImmutableList()); + + if (match3.isEmpty()) { + return ImmutableList.of(); } else { - return res2; + // add errors from best match (minimal badness) + ArgsMatchResult min = Collections.min(match3, Comparator.comparing(ArgsMatchResult::getBadness)); + for (CompileError c : min.getErrors()) { + fc.getErrorHandler().sendError(c); + } + + return match3.stream() + .map(ArgsMatchResult::getSig) + .collect(ImmutableList.toImmutableList()); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java index 5e331a4dd..3c621eb21 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeExprType.java @@ -97,4 +97,26 @@ public static WurstType calculate(TypeExprResolved e) { } + public static WurstType constraintType(TypeParamConstraint c) { + WurstType t = c.getConstraint().attrTyp(); + if (t instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) t; + + TypeParamDefs tps = wti.getDef().getTypeParameters(); + if (tps.isEmpty()) { + c.addError("Constraint " + t + " must be an interface type with at least one type parameter.\n" + + "Maybe change interface " + t + " to interface " + t + " ?"); + } else { + TypeParamDef lastTypeParam = Utils.getLast(tps); + TypeParamDef tp = c.parentTypeParam(); + // TODO check that type param is not yet set + wti = (WurstTypeInterface) wti.setTypeArgs(wti.getTypeArgBinding() + .set(lastTypeParam, new WurstTypeBoundTypeParam(lastTypeParam, new WurstTypeTypeParam(tp, false), c))); + } + return wti; + } else { + c.addError("Invalid type constraint " + t + ". Type constraint must be an interface type."); + return WurstTypeUnknown.instance(); + } + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java index 30ce51b69..0db7a71d2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrVarDefType.java @@ -3,7 +3,6 @@ import com.google.common.collect.Lists; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; -import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.types.*; import org.eclipse.jdt.annotation.Nullable; @@ -80,7 +79,7 @@ public static WurstType getParameterTypeFromClosureType(WShortParameter p, int p public static WurstTypeClass calculate(ClassDef c) { List typeArgs = Lists.newArrayList(); for (TypeParamDef tp : c.getTypeParameters()) { - WurstTypeTypeParam typParam = new WurstTypeTypeParam(tp); + WurstTypeTypeParam typParam = new WurstTypeTypeParam(tp, false); typeArgs.add(new WurstTypeBoundTypeParam(tp, typParam, tp)); } return new WurstTypeClass(c, typeArgs, true); @@ -155,13 +154,13 @@ public static WurstType calculate(FunctionDefinition f) { } public static WurstType calculate(TypeParamDef t) { - return new WurstTypeTypeParam(t); + return new WurstTypeTypeParam(t, true); } public static WurstTypeInterface calculate(InterfaceDef i) { List typeArgs = Lists.newArrayList(); for (TypeParamDef tp : i.getTypeParameters()) { - WurstTypeTypeParam tpType = new WurstTypeTypeParam(tp); + WurstTypeTypeParam tpType = new WurstTypeTypeParam(tp, false); typeArgs.add(new WurstTypeBoundTypeParam(tp, tpType, tp)); } return new WurstTypeInterface(i, typeArgs, true); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java index 07041b397..694e2391b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/DescriptionHtml.java @@ -70,8 +70,8 @@ public static String description(ConstructorDef constr) { descr = ""; } descr += "


" + - "construct(" + getParameterString(constr) + ") " - + "
" + "defined in class " + c.getName(); + "construct(" + getParameterString(constr) + ") " + + "
" + "defined in class " + c.getName(); return descr; } @@ -104,7 +104,7 @@ public static String description(NameDef n) { additionalProposalInfo = ""; } additionalProposalInfo += "

" + htmlType(n.attrTyp()) + " " + n.getName() - + "
" + "defined in " + nearestScopeName(n); + + "
" + "defined in " + nearestScopeName(n); return additionalProposalInfo; } @@ -130,7 +130,7 @@ public static String description(Annotation annotation) { FuncLink f = e.attrFuncLink(); if (f != null) { return "This is an overloaded operator:
" + - f.getDef().descriptionHtml(); + f.getDef().descriptionHtml(); } return null; } @@ -205,7 +205,7 @@ public static String description(ExprThis e) { public static String description(ExprTypeId exprTypeId) { return "typeId: returns the typeId of an object or class. The typeId is " - + "a unique number for each class in the same type hierarchy."; + + "a unique number for each class in the same type hierarchy."; } public static @Nullable String description(ExprUnary exprUnary) { @@ -214,7 +214,7 @@ public static String description(ExprTypeId exprTypeId) { public static @Nullable String description( - IdentifierWithTypeArgs identifierWithTypeArgs) { + IdentifierWithTypeArgs identifierWithTypeArgs) { return null; } @@ -223,13 +223,13 @@ public static String description(InitBlock initBlock) { } public static @Nullable String description( - IdentifierWithTypeParamDefs identifierWithTypeParamDefs) { + IdentifierWithTypeParamDefs identifierWithTypeParamDefs) { return null; } public static String description(ModAbstract modAbstract) { return "abstract: This function provides no implementation. Other classes have to provide " - + "an implementation for this method."; + + "an implementation for this method."; } public static String description(ModConstant modConstant) { @@ -243,7 +243,7 @@ public static String description(ModOverride m) { public static String description(ModStatic modStatic) { return "static: This function or variable is just like a function outside of a class. " - + "It is not bound to an instance. No dynamic dispatch is used."; + + "It is not bound to an instance. No dynamic dispatch is used."; } public static String description(ModuleUse m) { @@ -264,7 +264,7 @@ public static String description(ModuleUse m) { public static String description(OnDestroyDef s) { return "ondestroy block: These statements are executed when an object of this class " - + "is destroyed." + s.getSource().getLeftPos() + " - " + s.getSource().getRightPos(); + + "is destroyed." + s.getSource().getLeftPos() + " - " + s.getSource().getRightPos(); } public static @Nullable String description(StartFunctionStatement s) { @@ -335,7 +335,7 @@ public static String description(SwitchCase switchCase) { } public static String description( - SwitchDefaultCaseStatements switchDefaultCaseStatements) { + SwitchDefaultCaseStatements switchDefaultCaseStatements) { return "The default case for this switch statement"; } @@ -421,4 +421,27 @@ public static String description(SomeSuperConstructorCall s) { public static String description(NoTypeParamConstraints noTypeParamConstraints) { return "no type parameter constraints"; } + + public static String description(TypeParamConstraint tp) { + TypeParamDef d = findTypeParamDef(tp); + if (d == null) { + return "Type parameter constraint."; + } + return "Constraints the type parameter " + d.getName() + " to implement the type class " + tp.getConstraint().attrTyp(); + } + + private static TypeParamDef findTypeParamDef(TypeParamConstraint tp) { + Element e = tp; + while (e != null) { + if (e instanceof TypeParamDef) { + return (TypeParamDef) e; + } + e = e.getParent(); + } + return null; + } + + public static String description(InstanceDecl instanceDecl) { + return "Type class instance declaration."; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java index 233f2c61f..91a522cd2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/IsDynamicContext.java @@ -26,19 +26,23 @@ public static boolean calculate(Element origElem) { if (origElem.isSubtreeOf(a.getExtendedClass())) { return true; } + if (origElem.isSubtreeOf(a.getImplementsList())) { + return true; + } + if (origElem.isSubtreeOf(a.getTypeParameters())) { + return true; + } } if (elem instanceof InterfaceDef) { InterfaceDef a = (InterfaceDef) elem; if (origElem.isSubtreeOf(a.getExtendsList())) { return true; } - } - if (elem instanceof ClassDef) { - ClassDef a = (ClassDef) elem; - if (origElem.isSubtreeOf(a.getImplementsList())) { + if (origElem.isSubtreeOf(a.getTypeParameters())) { return true; } - } else if (elem instanceof ModuleUse) { + } + if (elem instanceof ModuleUse) { ModuleUse mu = (ModuleUse) elem; if (origElem.isSubtreeOf(mu.getTypeArgs())) { return true; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java index aeec4d6e2..08b6264c5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java @@ -158,6 +158,10 @@ public static ImmutableList calculate(InterfaceDef e) { return generic(e); } + public static ImmutableList calculate(InstanceDecl e) { + return generic(e); + } + public static ImmutableList calculate(ModuleDef e) { return generic(e); } @@ -201,4 +205,6 @@ public static ImmutableList calculate(ExprEmpty exprEmpty) { public static ImmutableList calculate(ExprIfElse e) { return generic(e); } + + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java index 49496a550..dd81dbdab 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java @@ -21,33 +21,35 @@ public class FuncLink extends DefLink { private final List parameterNames; private final List parameterTypes; private final VariableBinding mapping; + private final TypeParamConstraint typeParamConstraint; public FuncLink(Visibility visibility, WScope definedIn, List typeParams, - @Nullable WurstType receiverType, FunctionDefinition def, List parameterNames, List parameterTypes, WurstType returnType, VariableBinding mapping) { + @Nullable WurstType receiverType, FunctionDefinition def, List parameterNames, List parameterTypes, WurstType returnType, VariableBinding mapping, TypeParamConstraint typeParamConstraint) { super(visibility, definedIn, typeParams, receiverType); this.def = def; this.returnType = returnType; this.parameterTypes = parameterTypes; this.parameterNames = parameterNames; this.mapping = mapping; + this.typeParamConstraint = typeParamConstraint; } public static FuncLink create(FunctionDefinition func, WScope definedIn) { Visibility visibiliy = calcVisibility(definedIn, func); List typeParams = typeParams(func).collect(Collectors.toList()); List lParameterNames = func.getParameters().stream() - .map(WParameter::getName) - .collect(Collectors.toList()); + .map(WParameter::getName) + .collect(Collectors.toList()); List lParameterTypes = func.getParameters().stream() - .map(WParameter::attrTyp) - .collect(Collectors.toList()); + .map(WParameter::attrTyp) + .collect(Collectors.toList()); WurstType lreturnType = func.attrReturnTyp(); WurstType lreceiverType = calcReceiverType(definedIn, func); VariableBinding mapping = VariableBinding.emptyMapping(); if (func instanceof AstElementWithTypeParameters) { mapping = mapping.withTypeVariables(((AstElementWithTypeParameters) func).getTypeParameters()); } - return new FuncLink(visibiliy, definedIn, typeParams, lreceiverType, func, lParameterNames, lParameterTypes, lreturnType, mapping); + return new FuncLink(visibiliy, definedIn, typeParams, lreceiverType, func, lParameterNames, lParameterTypes, lreturnType, mapping, null); } @@ -77,7 +79,7 @@ public FuncLink withVisibility(Visibility newVis) { if (newVis == getVisibility()) { return this; } - return new FuncLink(newVis, getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(newVis, getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); } @@ -108,8 +110,8 @@ public String toString() { if (!typeParams.isEmpty()) { result.append("<"); result.append(typeParams.stream() - .map(TypeParamDef::getName) - .collect(Collectors.joining(", "))); + .map(TypeParamDef::getName) + .collect(Collectors.joining(", "))); result.append(">"); } result.append("("); @@ -118,10 +120,10 @@ public String toString() { result.append(returnType); WPos src = def.attrSource(); result.append(" (") - .append(src.getFile()) - .append(":") - .append(src.getLine()) - .append(")"); + .append(src.getFile()) + .append(":") + .append(src.getLine()) + .append(")"); return result.toString(); } @@ -155,9 +157,9 @@ public FuncLink withTypeArgBinding(Element context, VariableBinding binding) { if (changed) { // remove type parameters that are now bound: List newTypeParams = getTypeParams().stream() - .filter(tp -> !binding.contains(tp)) - .collect(Collectors.toList()); - return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, newReceiverType, def, parameterNames, newParamTypes, newReturnType, binding); + .filter(tp -> !binding.contains(tp)) + .collect(Collectors.toList()); + return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, newReceiverType, def, parameterNames, newParamTypes, newReturnType, binding, typeParamConstraint); } else { return this; } @@ -169,7 +171,7 @@ public DefLink withGenericTypeParams(List typeParams) { return this; } ImmutableList newTypeParams = Utils.concatLists(getTypeParams(), typeParams); - return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); } @Override @@ -179,7 +181,7 @@ public WurstType getTyp() { @Override public FuncLink withDef(NameDef def) { - return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), (FunctionDefinition) def, getParameterNames(), getParameterTypes(), getReturnType(), mapping); + return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), (FunctionDefinition) def, getParameterNames(), getParameterTypes(), getReturnType(), mapping, typeParamConstraint); } private WurstType adjustType(Element context, WurstType t, VariableBinding binding) { @@ -223,7 +225,7 @@ public boolean isVarargMethod() { public FuncLink withConfigDef() { FunctionDefinition def = (FunctionDefinition) this.def.attrConfigActualNameDef(); - return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); + return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); } public FuncLink hidingPrivate() { @@ -288,4 +290,16 @@ public VariableBinding getVariableBinding() { public boolean hasIfNotDefinedAnnotation() { return def.attrHasAnnotation("ifNotDefined"); } + + public FuncLink withReceiverType(WurstType newReceiverType) { + return new FuncLink(getVisibility(), def, typeParams, newReceiverType, def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); + } + + public FuncLink withTypeParamConstraint(TypeParamConstraint typeParamConstraint) { + return new FuncLink(getVisibility(), def, typeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping, typeParamConstraint); + } + + public @Nullable TypeParamConstraint getTypeParamConstraint() { + return typeParamConstraint; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java index 61946031c..df777d3a1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLink.java @@ -102,6 +102,20 @@ public List getTypeParams() { return typeParams; } + public boolean hasTypeClassConstraints() { + if (getDef() instanceof AstElementWithTypeParameters) + for (TypeParamDef tp : ((AstElementWithTypeParameters) getDef()).getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cs = (TypeParamConstraintList) tp.getTypeParamConstraints(); + if (!cs.isEmpty()) { + return true; + } + } + } + return false; + } + + public NameLink hidingPrivate() { if (visibility == Visibility.PRIVATE_HERE) { return withVisibility(Visibility.PRIVATE_OTHER); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java index bd054032b..d94649d3f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameLinks.java @@ -7,6 +7,7 @@ import com.google.common.collect.Multimap; import de.peeeq.wurstscript.WLogger; import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.types.WurstTypeClass; import de.peeeq.wurstscript.types.WurstTypeInterface; import de.peeeq.wurstscript.utils.Utils; @@ -16,10 +17,13 @@ import java.util.*; import java.util.Map.Entry; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class NameLinks { + + static private class OverrideCheckResult { // does this override some other function boolean doesOverride = false; @@ -48,6 +52,7 @@ public static ImmutableMultimap calculate(ClassOrModuleOrModule addNamesFromImplementedInterfaces(result, classType, overrideCheckResults); } + addTypeParametersIfAny(result::put, c); reportOverrideErrors(overrideCheckResults); return ImmutableMultimap.copyOf(result); } @@ -96,6 +101,22 @@ public static ImmutableMultimap calculate(InterfaceDef i) { Map> overrideCheckResults = initOverrideMap(result); addNamesFromExtendedInterfaces(result, i.attrTypI(), overrideCheckResults); reportOverrideErrors(overrideCheckResults); + addTypeParametersIfAny(result::put, i); + return ImmutableMultimap.copyOf(result); + } + + + public static ImmutableMultimap calculate(InstanceDecl i) { + Multimap result = HashMultimap.create(); + addDefinedNames(result, i, i.getMethods()); + Map> overrideCheckResults = initOverrideMap(result); + WurstType implementedI = i.getImplementedInterface().attrTyp(); + if (implementedI instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) implementedI; + addNewNameLinks(result, overrideCheckResults, wti.nameLinks(), false); + } + reportOverrideErrors(overrideCheckResults); + addTypeParametersIfAny(result::put, i); return ImmutableMultimap.copyOf(result); } @@ -170,9 +191,19 @@ public static ImmutableMultimap calculate(AstElementWithBody c) WScope s = (WScope) c; addVarDefIfAny(result, s); addParametersIfAny(result, s); + addTypeParametersIfAny(result::put, s); return result.build(); } + private static void addTypeParametersIfAny(BiConsumer result, WScope s) { + if (s instanceof AstElementWithTypeParameters) { + AstElementWithTypeParameters tps = (AstElementWithTypeParameters) s; + for (TypeParamDef tp : tps.getTypeParameters()) { + result.accept(tp.getName(), VarLink.create(tp, s)); + } + } + } + private static void addVarDefIfAny(Builder result, WScope s) { if (s instanceof LoopStatementWithVarDef) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java index 85a3cd482..4814abf1d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/OtherLink.java @@ -119,6 +119,11 @@ public CompilationUnit attrCompilationUnit() { return null; } + @Override + public @Nullable ClassOrInterfaceOrInstance attrNearestClassOrInterfaceOrInstance() { + return null; + } + @Override public @Nullable ClassOrModule attrNearestClassOrModule() { return null; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java index f734ff836..1f192f749 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/TypeNameLinks.java @@ -45,6 +45,12 @@ public static ImmutableMultimap calculate(InterfaceDef i) { return result.build(); } + public static ImmutableMultimap calculate(InstanceDecl i) { + ImmutableMultimap.Builder result = ImmutableSetMultimap.builder(); + addTypeParametersIfAny(result, i); + return result.build(); + } + public static ImmutableMultimap calculate(NativeFunc nativeFunc) { return ImmutableMultimap.of(); } @@ -116,4 +122,6 @@ private static void addJassTypes(ImmutableMultimap.Builder res public static ImmutableMultimap calculate(ExprClosure exprClosure) { return ImmutableMultimap.of(); } + + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java index aaf01b603..cbe2d6837 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/VarLink.java @@ -145,7 +145,7 @@ public VarLink withTypeArgBinding(Element context, VariableBinding binding) { } Deferred newType = type.map(oldType -> oldType.setTypeArgs(binding)); boolean changed = newType != type; - WurstType newReceiverType = getReceiverType().setTypeArgs(binding); + WurstType newReceiverType = getReceiverType() == null ? null : getReceiverType().setTypeArgs(binding); changed |= newReceiverType != getReceiverType(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java index 93bb07a9d..3df967d94 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/prettyPrint/PrettyPrinter.java @@ -81,8 +81,8 @@ public static void prettyPrint(ConstructorDef e, Spacer spacer, StringBuilder sb public static void prettyPrint(ConstructorDefs e, Spacer spacer, StringBuilder sb, int indent) { for (ConstructorDef constructorDef : e) { if (!constructorDef.getParameters().isEmpty() - || constructorDef.getSuperConstructorCall() instanceof SomeSuperConstructorCall - || constructorDef.getBody().size() > 2) { + || constructorDef.getSuperConstructorCall() instanceof SomeSuperConstructorCall + || constructorDef.getBody().size() > 2) { constructorDef.prettyPrint(spacer, sb, indent); } } @@ -388,6 +388,17 @@ public static void prettyPrint(InterfaceDef e, Spacer spacer, StringBuilder sb, e.getMethods().prettyPrint(spacer, sb, indent); } + + public static void prettyPrint(InstanceDecl e, Spacer spacer, StringBuilder sb, int indent) { + printIndent(sb, indent); + sb.append("instance"); + e.getTypeParameters().prettyPrint(spacer, sb, indent); + spacer.addSpace(sb); + e.getImplementedInterface().prettyPrint(spacer, sb, indent); + sb.append("\n"); + e.getMethods().prettyPrint(spacer, sb, indent + 1); + } + public static void prettyPrint(JassGlobalBlock e, Spacer spacer, StringBuilder sb, int indent) { } @@ -570,6 +581,12 @@ public static void prettyPrint(TypeExprArray e, Spacer spacer, StringBuilder sb, } public static void prettyPrint(TypeExprList e, Spacer spacer, StringBuilder sb, int indent) { + if (e.isEmpty()) { + return; + } + sb.append("<"); + commaSeparatedList(e, spacer, sb, indent); + sb.append(">"); } public static void prettyPrint(TypeExprResolved e, Spacer spacer, StringBuilder sb, int indent) { @@ -723,11 +740,11 @@ public static void prettyPrint(Identifier identifier, Spacer spacer, StringBuild public static void prettyPrint(ExprIfElse e, Spacer spacer, StringBuilder sb, int indent) { sb.append("("); - e.getCond().prettyPrint(spacer, sb, indent+1); + e.getCond().prettyPrint(spacer, sb, indent + 1); sb.append(" ? "); - e.getIfTrue().prettyPrint(spacer, sb, indent+1); + e.getIfTrue().prettyPrint(spacer, sb, indent + 1); sb.append(" : "); - e.getIfFalse().prettyPrint(spacer, sb, indent+1); + e.getIfFalse().prettyPrint(spacer, sb, indent + 1); sb.append(")"); } @@ -752,4 +769,21 @@ public static void prettyPrint(SomeSuperConstructorCall c, Spacer spacer, String public static void prettyPrint(NoTypeParamConstraints noTypeParamConstraints, Spacer spacer, StringBuilder sb, int indent) { // nothing } + + public static String print(Element element) { + Spacer spacer = new DefaultSpacer(); + StringBuilder sb = new StringBuilder(); + element.prettyPrint(spacer, sb, 0); + return sb.toString(); + } + + public static void prettyPrint(TypeParamConstraint t, Spacer spacer, StringBuilder sb, int indent) { + t.getConstraint().prettyPrint(spacer, sb, indent); + } + + public static void prettyPrint(TypeParamConstraintList typeParamConstraints, Spacer spacer, StringBuilder sb, int indent) { + commaSeparatedList(typeParamConstraints, spacer, sb, indent); + } + + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index 26396c102..580979c4b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -12,6 +12,7 @@ import org.eclipse.jdt.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -29,21 +30,21 @@ public static ILconst eval(ImFuncRef e, ProgramState globalState, LocalState loc public static @Nullable ILconst eval(ImFunctionCall e, ProgramState globalState, LocalState localState) { ImFunction f = e.getFunc(); ImExprs arguments = e.getArguments(); - return evaluateFunc(globalState, localState, f, arguments, e); + return evaluateFunc(globalState, localState, f, e.getTypeArguments(), arguments, e); } public static @Nullable ILconst evaluateFunc(ProgramState globalState, - LocalState localState, ImFunction f, List args2, Element trace) { + LocalState localState, ImFunction f, List typeArguments, List args2, Element trace) { ILconst[] args = new ILconst[args2.size()]; for (int i = 0; i < args2.size(); i++) { args[i] = args2.get(i).evaluate(globalState, localState); } - return evaluateFunc(globalState, f, trace, args); + return evaluateFunc(globalState, f, trace, typeArguments, args); } @Nullable - private static ILconst evaluateFunc(ProgramState globalState, ImFunction f, Element trace, ILconst[] args) { - LocalState r = ILInterpreter.runFunc(globalState, f, trace, args); + private static ILconst evaluateFunc(ProgramState globalState, ImFunction f, Element trace, List typeArguments, ILconst[] args) { + LocalState r = ILInterpreter.runFunc(globalState, f, trace, typeArguments, args); return r.getReturnVal(); } @@ -185,26 +186,30 @@ public static ILconst eval(ImVarArrayAccess e, ProgramState globalState, LocalSt List args = mc.getArguments(); + ImMethod mostPrecise = findMostPreciseMethod(mc.attrTrace(), globalState, receiver, mc.getMethod()); - ImMethod mostPrecise = mc.getMethod(); + // execute most precise method + ILconst[] eargs = new ILconst[args.size() + 1]; + eargs[0] = receiver; + for (int i = 0; i < args.size(); i++) { + eargs[i + 1] = args.get(i).evaluate(globalState, localState); + } + return evaluateFunc(globalState, mostPrecise.getImplementation(), mc, mc.getTypeArguments(), eargs); + } + private static ImMethod findMostPreciseMethod(de.peeeq.wurstscript.ast.Element position, ProgramState globalState, ILconstObject receiver, ImMethod originalMethod) { // find correct implementation: - for (ImMethod m : mc.getMethod().getSubMethods()) { + ImMethod mostPrecise = originalMethod; + for (ImMethod m : originalMethod.getSubMethods()) { if (m.attrClass().isSubclassOf(mostPrecise.attrClass())) { - if (globalState.isInstanceOf(receiver, m.attrClass(), mc.attrTrace())) { + if (globalState.isInstanceOf(receiver, m.attrClass(), position)) { // found more precise method mostPrecise = m; } } } - // execute most precise method - ILconst[] eargs = new ILconst[args.size() + 1]; - eargs[0] = receiver; - for (int i = 0; i < args.size(); i++) { - eargs[i + 1] = args.get(i).evaluate(globalState, localState); - } - return evaluateFunc(globalState, mostPrecise.getImplementation(), mc, eargs); + return mostPrecise; } public static ILconst eval(ImMemberAccess ma, ProgramState globalState, LocalState localState) { @@ -392,11 +397,6 @@ public ILconst get() { } - public static ILconst eval(ImTypeVarDispatch e, ProgramState globalState, LocalState localState) { - // TODO store type arguments in localState with the required dispatch functions - throw new InterpreterException(e.attrTrace(), "Cannot evaluate " + e); - } - public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState localState) { ILconst res = imCast.getExpr().evaluate(globalState, localState); if (TypesHelper.isIntType(imCast.getToType())) { @@ -410,7 +410,8 @@ public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState l } } if (res instanceof ILconstInt) { - if (imCast.getToType() instanceof ImClassType) { + if (imCast.getToType() instanceof ImClassType + || imCast.getToType() instanceof ImTypeVarRef) { return globalState.getObjectByIndex(((ILconstInt) res).getVal()); } if (imCast.getToType() instanceof IlConstHandle) { @@ -419,4 +420,14 @@ public static ILconst eval(ImCast imCast, ProgramState globalState, LocalState l } return res; } + + public static ILconst eval(ImTypeClassDictValue e, ProgramState globalState, LocalState localState) { + ILconstObject obj = globalState.allocate(e.getClazz(), e.attrTrace()); + int i = 0; + for (ImExpr arg : e.getArguments()) { + ILconst argV = arg.evaluate(globalState, localState); + obj.set(e.getClazz().getClassDef().getFields().get(i), Collections.emptyList(), argV); + } + return obj; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 314fe1dd0..efa7b890e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -19,6 +19,8 @@ import java.io.File; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; public class ILInterpreter implements AbstractInterpreter { @@ -38,7 +40,7 @@ public ILInterpreter(ImProg prog, WurstGui gui, @Nullable File mapFile, boolean } public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullable Element caller, - ILconst... args) { + List typeArguments, ILconst... args) { if (Thread.currentThread().isInterrupted()) { throw new InterpreterException(globalState, "Execution interrupted"); } @@ -77,6 +79,7 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab } LocalState localState = new LocalState(); + int i = 0; for (ImVar p : f.getParameters()) { localState.setVal(p, args[i]); @@ -156,7 +159,12 @@ private static LocalState runBuiltinFunction(ProgramState globalState, ImFunctio StringBuilder errors = new StringBuilder(); for (NativesProvider natives : globalState.getNativeProviders()) { try { - return new LocalState(natives.invoke(f.getName(), args)); + ILconst res = natives.invoke(f.getName(), args); + if (res == null && !(f.getReturnType() instanceof ImVoid)) { + // use default value for function stub + res = ImHelper.defaultValueForComplexType(f.getReturnType()).evaluate(globalState, new LocalState()); + } + return new LocalState(res); } catch (NoSuchNativeException e) { errors.append("\n").append(e.getMessage()); // ignore @@ -189,7 +197,7 @@ public LocalState executeFunction(String funcName, @Nullable Element trace) { globalState.resetStackframes(); for (ImFunction f : prog.getFunctions()) { if (f.getName().equals(funcName)) { - return runFunc(globalState, f, trace); + return runFunc(globalState, f, trace, Collections.emptyList()); } } @@ -203,7 +211,7 @@ public void runVoidFunc(ImFunction f, @Nullable Element trace) { // this should only happen because of added stacktrace parameter args = new ILconstString[]{new ILconstString("initial call")}; } - runFunc(globalState, f, trace, args); + runFunc(globalState, f, trace, Collections.emptyList(), args); } public Element getLastStatement() { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java index b2d093e1e..2039e60ef 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/LocalState.java @@ -1,8 +1,15 @@ package de.peeeq.wurstscript.intermediatelang.interpreter; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; import de.peeeq.wurstscript.intermediatelang.ILconst; +import de.peeeq.wurstscript.jassIm.*; +import io.vavr.control.Either; import org.eclipse.jdt.annotation.Nullable; +import java.util.List; +import java.util.Map; + public class LocalState extends State { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java index a93ce4332..46d193eca 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java @@ -5,6 +5,7 @@ import de.peeeq.wurstio.jassinterpreter.InterpreterException; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.gui.WurstGui; import de.peeeq.wurstscript.intermediatelang.*; import de.peeeq.wurstscript.jassIm.*; @@ -102,7 +103,9 @@ public void deallocate(ILconstObject obj, ImClass clazz, Element trace) { public void assertAllocated(ILconstObject obj, Element trace) { if (obj == null) { - throw new InterpreterException(trace, "Null pointer dereference"); + throw new InterpreterException(trace, "Null pointer dereference in " + + PrettyPrinter.print(trace) + ); } if (obj.isDestroyed()) { throw new InterpreterException(trace, "Object already destroyed"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java index 73ebdb506..08f5f4c5b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/RunStatement.java @@ -52,6 +52,9 @@ public static void run(ImReturn s, ProgramState globalState, LocalState localSta if (s.getReturnValue() instanceof ImExpr) { ImExpr e = (ImExpr) s.getReturnValue(); r = e.evaluate(globalState, localState); + if (r == null) { + throw new InterpreterException(s.getTrace(), "Returned value was null"); + } } throw new ReturnException(r); } @@ -59,6 +62,9 @@ public static void run(ImReturn s, ProgramState globalState, LocalState localSta public static void run(ImSet s, ProgramState globalState, LocalState localState) { ILaddress v = s.getLeft().evaluateLvalue(globalState, localState); ILconst right = s.getRight().evaluate(globalState, localState); + if (right == null) { + throw new InterpreterException(s.getTrace(), "Right hand side of assignment was null."); + } v.set(right); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java index 4c3f2a080..680e2ad06 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/State.java @@ -4,10 +4,7 @@ import de.peeeq.wurstio.jassinterpreter.InterpreterException; import de.peeeq.wurstscript.intermediatelang.ILconst; import de.peeeq.wurstscript.intermediatelang.ILconstArray; -import de.peeeq.wurstscript.jassIm.ImArrayLikeType; -import de.peeeq.wurstscript.jassIm.ImArrayTypeMulti; -import de.peeeq.wurstscript.jassIm.ImType; -import de.peeeq.wurstscript.jassIm.ImVar; +import de.peeeq.wurstscript.jassIm.*; import org.eclipse.jdt.annotation.Nullable; import java.util.List; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java index d012731cf..b0d4c7274 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/FunctionSplitter.java @@ -100,10 +100,6 @@ private List> split(List body) { private int estimateFuel(ImStmt s) { return s.match(new ImStmt.Matcher() { - @Override - public Integer case_ImTypeVarDispatch(ImTypeVarDispatch s) { - return estimateFuel(s.getArguments()) + 100; - } @Override public Integer case_ImDealloc(ImDealloc s) { @@ -238,6 +234,11 @@ public Integer case_ImIf(ImIf s) { return 1 + estimateFuel(s.getCondition()) + Math.max(estimateFuel(s.getThenBlock()), estimateFuel(s.getElseBlock())); } + @Override + public Integer case_ImTypeClassDictValue(ImTypeClassDictValue imTypeClassDictValue) { + return 1; + } + @Override public Integer case_ImCast(ImCast s) { return estimateFuel(s.getExpr()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java index b8c341a10..202ae2895 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SideEffectAnalyzer.java @@ -61,6 +61,11 @@ public Boolean case_ImTupleSelection(ImTupleSelection e) { return quickcheckHasSideeffects(e.getTupleExpr()); } + @Override + public Boolean case_ImTypeClassDictValue(ImTypeClassDictValue e) { + return e.getArguments().stream().anyMatch(SideEffectAnalyzer::quickcheckHasSideeffects); + } + @Override public Boolean case_ImInstanceof(ImInstanceof e) { return quickcheckHasSideeffects(e.getObj()); @@ -96,11 +101,6 @@ public Boolean case_ImGetStackTrace(ImGetStackTrace imGetStackTrace) { return true; } - @Override - public Boolean case_ImTypeVarDispatch(ImTypeVarDispatch imTypeVarDispatch) { - return true; - } - @Override public Boolean case_ImOperatorCall(ImOperatorCall e) { return e.getArguments().stream().anyMatch(SideEffectAnalyzer::quickcheckHasSideeffects); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java index c9b181428..6720d06e2 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jass/AntlrJassParseTreeTransformer.java @@ -18,15 +18,17 @@ public class AntlrJassParseTreeTransformer { - private String file; - private ErrorHandler cuErrorHandler; - private LineOffsets lineOffsets; + private final WPos.FileInfo fileInfo; + private final String file; + private final ErrorHandler cuErrorHandler; + private final LineOffsets lineOffsets; public AntlrJassParseTreeTransformer(String file, ErrorHandler cuErrorHandler, LineOffsets lineOffsets) { this.file = file; this.cuErrorHandler = cuErrorHandler; this.lineOffsets = lineOffsets; + this.fileInfo = new WPos.FileInfo(file, lineOffsets); } public CompilationUnit transform(JassParser.CompilationUnitContext cu) { @@ -70,7 +72,7 @@ private String text(@Nullable Token t) { private Identifier text(@Nullable ParserRuleContext c) { if (c == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(c), c.getText()); } @@ -496,12 +498,12 @@ private WParameter transformFormalParameter(JassParser.FormalParamContext p, } private WPos source(ParserRuleContext p) { - return new WPos(file, lineOffsets, p.start.getStartIndex(), + return new WPos(fileInfo, p.start.getStartIndex(), p.stop.getStopIndex() + 1); } private WPos source(Token p) { - return new WPos(file, lineOffsets, p.getStartIndex(), p.getStopIndex() + 1); + return new WPos(fileInfo, p.getStartIndex(), p.getStopIndex() + 1); } class FuncSig { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java index 4f7c3460f..653c90e05 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/jurst/AntlrJurstParseTreeTransformer.java @@ -20,9 +20,10 @@ public class AntlrJurstParseTreeTransformer { - private String file; - private ErrorHandler cuErrorHandler; - private LineOffsets lineOffsets; + private final WPos.FileInfo fileInfo; + private final String file; + private final ErrorHandler cuErrorHandler; + private final LineOffsets lineOffsets; private boolean isJassCode = true; public AntlrJurstParseTreeTransformer(String file, @@ -30,6 +31,7 @@ public AntlrJurstParseTreeTransformer(String file, this.file = file; this.cuErrorHandler = cuErrorHandler; this.lineOffsets = lineOffsets; + this.fileInfo = new WPos.FileInfo(file, lineOffsets); } public CompilationUnit transform(CompilationUnitContext cu) { @@ -74,7 +76,7 @@ private JassToplevelDeclaration transformJassToplevelDecl( private Identifier text(@Nullable ParserRuleContext c) { if (c == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(c), c.getText()); } @@ -1272,12 +1274,12 @@ private WImport transformImport(WImportContext i) { } private WPos source(ParserRuleContext p) { - return new WPos(file, lineOffsets, p.start.getStartIndex(), + return new WPos(fileInfo, p.start.getStartIndex(), p.stop.getStopIndex() + 1); } private WPos source(Token p) { - return new WPos(file, lineOffsets, p.getStartIndex(), p.getStopIndex() + 1); + return new WPos(fileInfo, p.getStartIndex(), p.getStopIndex() + 1); } class FuncSig { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java index 6a020e389..902c12af8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/WPos.java @@ -7,24 +7,45 @@ import java.util.regex.Pattern; public class WPos { - private final String file; - private final @Nullable LineOffsets lineOffsets; + + private FileInfo fileInfo; private final int leftPos; private final int rightPos; - public WPos(String file, @Nullable LineOffsets lineOffsets, int leftPos, int rightPos) { - this.file = file; - this.lineOffsets = lineOffsets; + public static WPos noSource() { + return new WPos(new FileInfo("", null), 0, 0); + } + + public FileInfo getFileInfo() { + return fileInfo; + } + + public static class FileInfo { + private final String file; + private final @Nullable LineOffsets lineOffsets; + + public FileInfo(String file, @Nullable LineOffsets lineOffsets) { + this.file = file; + this.lineOffsets = lineOffsets; + } + } + + public WPos(String file, LineOffsets lo, int leftPos, int rightPos) { + this(new FileInfo(file, lo), leftPos, rightPos); + } + + public WPos(FileInfo fileInfo, int leftPos, int rightPos) { + this.fileInfo = fileInfo; this.leftPos = leftPos; this.rightPos = rightPos; } public String getFile() { - return file; + return fileInfo.file; } public @Nullable LineOffsets getLineOffsets() { - return lineOffsets; + return fileInfo.lineOffsets; } public int getLeftPos() { @@ -37,35 +58,35 @@ public int getRightPos() { public int getLine() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getLine(leftPos); } public int getEndLine() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getLine(rightPos); } public int getStartColumn() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getColumn(leftPos); } public int getEndColumn() { - LineOffsets lo = lineOffsets; + LineOffsets lo = getLineOffsets(); if (lo == null) return 0; return lo.getColumn(rightPos); } public WPos withRightPos(int rightPos) { - return new WPos(file, lineOffsets, leftPos, rightPos); + return new WPos(fileInfo, leftPos, rightPos); } @Override @@ -75,13 +96,13 @@ public String toString() { } public String print() { - return "[" + file + " line " + getLine() + "]"; + return "[" + getFile() + " line " + getLine() + "]"; } public String printShort() { Pattern p = Pattern.compile("^.*[/\\\\]([^/\\\\]+)\\.[^.]*$"); - String shortFile = file; - Matcher m = p.matcher(file); + String shortFile = getFile(); + Matcher m = p.matcher(getFile()); if (m.find()) { shortFile = m.group(1); } @@ -89,11 +110,11 @@ public String printShort() { } public WPos withLeftPos(int leftPos) { - return new WPos(file, lineOffsets, leftPos, rightPos); + return new WPos(fileInfo, leftPos, rightPos); } public WPos withFile(String file) { - return new WPos(file, lineOffsets, leftPos, rightPos); + return new WPos(new FileInfo(file, fileInfo.lineOffsets), leftPos, rightPos); } public String shortFile() { @@ -107,7 +128,7 @@ public String shortFile() { * makes this position artificial by setting the rightPost = leftPos-1 */ public WPos artificial() { - return new WPos(file, lineOffsets, leftPos, leftPos - 1); + return new WPos(fileInfo, leftPos, leftPos - 1); } /** diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java index 9430f36ac..eec68104e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/AntlrWurstParseTreeTransformer.java @@ -22,15 +22,17 @@ public class AntlrWurstParseTreeTransformer { - private String file; - private ErrorHandler cuErrorHandler; - private LineOffsets lineOffsets; + private final String file; + private final ErrorHandler cuErrorHandler; + private final LineOffsets lineOffsets; + private final WPos.FileInfo fileInfo; public AntlrWurstParseTreeTransformer(String file, ErrorHandler cuErrorHandler, LineOffsets lineOffsets) { this.file = file; this.cuErrorHandler = cuErrorHandler; this.lineOffsets = lineOffsets; + this.fileInfo = new WPos.FileInfo(file, lineOffsets); } public CompilationUnit transform(CompilationUnitContext cu) { @@ -74,14 +76,14 @@ private JassToplevelDeclaration transformJassToplevelDecl( private Identifier text(@Nullable Token t) { if (t == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(t), t.getText()); } private Identifier text(@Nullable ParserRuleContext c) { if (c == null) { - return Ast.Identifier(new WPos(file, lineOffsets, 1, 0), ""); + return Ast.Identifier(new WPos(fileInfo, 1, 0), ""); } return Ast.Identifier(source(c), c.getText()); } @@ -288,6 +290,8 @@ private WPackage transformPackage(WpackageContext p) { return transformTupleDef(e.tupleDef()); } else if (e.extensionFuncDef() != null) { return transformExtensionFuncDef(e.extensionFuncDef()); + } else if (e.instanceDeclaration() != null) { + return transformInstanceDeclaration(e.instanceDeclaration()); } if (e.exception != null) { @@ -301,6 +305,37 @@ private WPackage transformPackage(WpackageContext p) { } } + private WEntity transformInstanceDeclaration(InstanceDeclarationContext i) { + return Ast.InstanceDecl( + source(i), + transformModifiers(i.modifiersWithDoc()), + transformInstanceTypeParams(i.params), + transformTypeExpr(i.implemented), + transformFuncDefs(i.funcDef()) + ); + } + + private TypeParamDefs transformInstanceTypeParams(List params) { + TypeParamDefs result = Ast.TypeParamDefs(); + if (params != null) { + for (TypeParamContext p : params) { + result.add(transformTypeParam(p, true)); + } + } + return result; + } + + private FuncDefs transformFuncDefs(List funcDef) { + FuncDefs res = Ast.FuncDefs(); + if (funcDef == null) { + return res; + } + for (FuncDefContext f : funcDef) { + res.add(transformFuncDef(f)); + } + return res; + } + private WEntity transformExtensionFuncDef(ExtensionFuncDefContext f) { WPos src = source(f); Modifiers modifiers = transformModifiers(f.modifiersWithDoc()); @@ -1290,25 +1325,30 @@ private TypeParamDefs transformTypeParams(TypeParamsContext typeParams) { TypeParamDefs result = Ast.TypeParamDefs(); if (typeParams != null && typeParams.params != null) { for (TypeParamContext p : typeParams.params) { - result.add(transformTypeParam(p)); + result.add(transformTypeParam(p, false)); } } return result; } - private TypeParamDef transformTypeParam(TypeParamContext p) { + private TypeParamDef transformTypeParam(TypeParamContext p, boolean defaultsToNewGenerics) { Modifiers modifiers = Ast.Modifiers(); - TypeParamConstraints typeParamClasses = tranformTypeParamConstraints(p.typeParamConstraints()); + TypeParamConstraints typeParamClasses = tranformTypeParamConstraints(p.typeParamConstraints(), defaultsToNewGenerics); return Ast.TypeParamDef(source(p), modifiers, text(p.name), typeParamClasses); } - private TypeParamConstraints tranformTypeParamConstraints(TypeParamConstraintsContext tc) { + private TypeParamConstraints tranformTypeParamConstraints(TypeParamConstraintsContext tc, boolean defaultsToNewGenerics) { if (tc == null) { - return Ast.NoTypeParamConstraints(); + if (defaultsToNewGenerics) { + return Ast.TypeParamConstraintList(); + } else { + return Ast.NoTypeParamConstraints(); + } } - TypeExprList res = Ast.TypeExprList(); + TypeParamConstraintList res = Ast.TypeParamConstraintList(); for (TypeExprContext t : tc.constraints) { - res.add(transformTypeExpr(t)); + TypeExpr te = transformTypeExpr(t); + res.add(Ast.TypeParamConstraint(te)); } return res; } @@ -1329,11 +1369,11 @@ private WPos source(ParserRuleContext p) { } else { stopIndex = p.stop.getStopIndex() + 1; } - return new WPos(file, lineOffsets, p.start.getStartIndex(), stopIndex); + return new WPos(fileInfo, p.start.getStartIndex(), stopIndex); } private WPos source(Token p) { - return new WPos(file, lineOffsets, p.getStartIndex(), p.getStopIndex() + 1); + return new WPos(fileInfo, p.getStartIndex(), p.getStopIndex() + 1); } class FuncSig { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java index c9277fc5d..3d6b5ed47 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java @@ -23,7 +23,7 @@ public void compressNames() { public void compressGlobals() { for (final ImVar global : prog.getGlobals()) { - if (global.getIsBJ()) { + if (global.isBJ()) { // no not rename bj constants continue; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java index 087cac4ef..00f0ffb3b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImInliner.java @@ -110,7 +110,7 @@ private void inlineCall(ImFunction f, Element parent, int parentI, ImFunctionCal for (int pi = 0; pi < called.getParameters().size(); pi++) { ImVar param = called.getParameters().get(pi); ImExpr arg = args.get(pi); - ImVar tempVar = JassIm.ImVar(arg.attrTrace(), param.getType(), param.getName(), false); + ImVar tempVar = JassIm.ImVar(arg.attrTrace(), param.getType(), param.getName(), Collections.emptyList()); f.getLocals().add(tempVar); varSubtitutions.put(param, tempVar); // set temp var @@ -118,7 +118,7 @@ private void inlineCall(ImFunction f, Element parent, int parentI, ImFunctionCal } // add locals for (ImVar l : called.getLocals()) { - ImVar newL = JassIm.ImVar(l.getTrace(), l.getType(), l.getName(), false); + ImVar newL = JassIm.ImVar(l.getTrace(), l.getType(), l.getName(), Collections.emptyList()); f.getLocals().add(newL); varSubtitutions.put(l, newL); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java index ace873313..eb499dbe3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/NullSetter.java @@ -5,6 +5,7 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -114,7 +115,7 @@ private void handleReturnStmt(final ImFunction f, if (exprContainsVar(returnExpr, handleVars)) { // if the returnExpr contains some handleVar, we have to add a temporary var - ImVar tempReturn = JassIm.ImVar(imReturn.attrTrace(), returnExpr.attrTyp(), f.getName() + "tempReturn", false); + ImVar tempReturn = JassIm.ImVar(imReturn.attrTrace(), returnExpr.attrTyp(), f.getName() + "tempReturn", Collections.emptyList()); if (isHandleType(returnExpr.attrTyp())) { // use global variables for handle types prog.getGlobals().add(tempReturn); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java index 6545d6694..358edc43f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ExprTranslation.java @@ -140,11 +140,11 @@ public static JassExpr translate(ImCompiletimeExpr e, ImToJassTranslator transla "Enable '-runcompiletimefunctions' to evaluate compiletime expressions."); } - public static JassExpr translate(ImTypeVarDispatch e, ImToJassTranslator translator) { - throw new CompileError(e, "Typevar dispatch not eliminated."); - } - public static JassExpr translate(ImCast imCast, ImToJassTranslator translator) { return imCast.getExpr().translate(translator); } + + public static JassExpr translate(ImTypeClassDictValue e, ImToJassTranslator translator) { + throw new CompileError(e.getTrace().attrSource(), "Dependent type classes should be elimnated: " + e); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java index 26695972d..3038f5bd7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttrType.java @@ -4,7 +4,6 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.*; -import java.util.ArrayList; import java.util.List; public class ImAttrType { @@ -212,11 +211,11 @@ public static ImType getType(ImCompiletimeExpr e) { return e.getExpr().attrTyp(); } - public static ImType getType(ImTypeVarDispatch e) { - return e.getTypeClassFunc().getReturnType(); - } - public static ImType getType(ImCast imCast) { return imCast.getToType(); } + + public static ImType getType(ImTypeClassDictValue e) { + return e.getClazz(); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java index 6d9869542..cfb02a560 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java @@ -208,12 +208,12 @@ JassVar getJassVarFor(ImVar v) { } else { if (isGlobal(v) && v.getType() instanceof ImSimpleType) { JassExpr initialVal = ImHelper.defaultValueForType((ImSimpleType) v.getType()).translate(this); - result = JassAst.JassInitializedVar(type, name, initialVal, v.getIsBJ()); + result = JassAst.JassInitializedVar(type, name, initialVal, v.isBJ()); } else { result = JassAst.JassSimpleVar(type, name); } } - if (isGlobal(v) && (!v.getIsBJ() || result instanceof JassInitializedVar)) { + if (isGlobal(v) && (!v.isBJ() || result instanceof JassInitializedVar)) { prog.getGlobals().add(result); } jassVars.put(v, result); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java index d5acdfa81..1f268b4bd 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeEquality.java @@ -1,6 +1,7 @@ package de.peeeq.wurstscript.translation.imtojass; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.utils.Utils; public class TypeEquality { @@ -75,15 +76,13 @@ public static boolean isEqualType(ImClassType c, ImType other) { if (!x.getType().equalsType(y.getType())) { return false; } - if (!x.getTypeClassBinding().equals(y.getTypeClassBinding())) { - return false; - } } return true; } return false; } + public static boolean isEqualType(ImAnyType t, ImType other) { return other instanceof ImAnyType; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java index 0f9b9b455..40ac9fda5 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java @@ -53,7 +53,9 @@ public ImType case_ImArrayType(ImArrayType t) { public ImType case_ImClassType(ImClassType t) { ImTypeArguments args = t.getTypeArguments() .stream() - .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this), ta.getTypeClassBinding())) + .map(ta -> { + return JassIm.ImTypeArgument(ta.getType().match(this)); + }) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(t.getClassDef(), args); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java index dc5e6669d..c14182a06 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/AssertProperty.java @@ -43,16 +43,11 @@ public void check(Element e) { ImFunction f = (ImFunction) e; currentFunction = f; checkType(e, (f).getReturnType()); - } else if (e instanceof ImTypeClassFunc) { - checkType(e, ((ImTypeClassFunc) e).getReturnType()); } else if (e instanceof ImMethod) { checkType(e, ((ImMethod) e).getMethodClass()); checkRooted(e, ((ImMethod) e).getImplementation()); } else if (e instanceof ImVarargLoop) { checkRooted(e, ((ImVarargLoop) e).getLoopVar()); - } else if (e instanceof ImTypeVarDispatch) { - checkRooted(e, ((ImTypeVarDispatch) e).getTypeClassFunc()); - checkRooted(e, ((ImTypeVarDispatch) e).getTypeVariable()); } else if (e instanceof ImVarAccess) { checkRooted(e, ((ImVarAccess) e).getVar()); } else if (e instanceof ImVarArrayAccess) { @@ -96,7 +91,7 @@ public void checkRooted(Element location, Element el) { } Element parent = e.getParent(); if (parent == null) { - break; + throw new CompileError(location,"Element " + el + " has no parent"); } checkContains(location, parent, e); if (parent instanceof ImFunction && parent != currentFunction) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java index e525cb44c..427daba9e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassManagementVars.java @@ -1,13 +1,10 @@ package de.peeeq.wurstscript.translation.imtranslation; import de.peeeq.wurstscript.ast.Element; -import de.peeeq.wurstscript.jassIm.ImClass; -import de.peeeq.wurstscript.jassIm.ImProg; -import de.peeeq.wurstscript.jassIm.ImVar; -import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.TypesHelper; -import java.lang.reflect.Type; +import java.util.Collections; public class ClassManagementVars { /** @@ -33,16 +30,16 @@ public class ClassManagementVars { public ClassManagementVars(ImClass repClass, ImTranslator translator) { Element tr = repClass.getTrace(); ImProg prog = translator.getImProg(); - free = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_nextFree", false); + free = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_nextFree", Collections.emptyList()); prog.getGlobals().add(free); - freeCount = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_firstFree", false); - translator.addGlobalWithInitalizer(freeCount, JassIm.ImIntVal(0)); + freeCount = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_firstFree", Collections.emptyList()); + translator.addGlobalWithInitializerFront(freeCount, JassIm.ImIntVal(0)); - maxIndex = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_maxIndex", false); - translator.addGlobalWithInitalizer(maxIndex, JassIm.ImIntVal(0)); + maxIndex = JassIm.ImVar(tr, TypesHelper.imInt(), repClass.getName() + "_maxIndex", Collections.emptyList()); + translator.addGlobalWithInitializerFront(maxIndex, JassIm.ImIntVal(0)); - typeId = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_typeId", false); + typeId = JassIm.ImVar(tr, JassIm.ImArrayType(TypesHelper.imInt()), repClass.getName() + "_typeId", Collections.emptyList()); prog.getGlobals().add(typeId); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java index d96a3afef..918fa2b43 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassTranslator.java @@ -14,7 +14,9 @@ import de.peeeq.wurstscript.jassIm.ImVarAccess; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Pair; +import de.peeeq.wurstscript.utils.Utils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,6 +35,7 @@ public class ClassTranslator { private ImClass imClass; private ImProg prog; private ImFunction classInitFunc; + private final List typeConstraintVars = new ArrayList<>(); public ClassTranslator(ClassDef classDef, ImTranslator translator) { this.classDef = classDef; @@ -70,6 +73,7 @@ private void translate() { List subClasses = translator.getSubClasses(classDef); // order is important here + addTypeConstraintVars(classDef); translateMethods(classDef, subClasses); translateVars(classDef); translateClassInitFunc(); @@ -79,6 +83,18 @@ private void translate() { } + private void addTypeConstraintVars(ClassDef classDef) { + for (TypeParamDef tp : classDef.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cList = (TypeParamConstraintList) tp.getTypeParamConstraints(); + for (TypeParamConstraint c : cList) { + ImVar v = translator.getTypeClassParamFor(c); + typeConstraintVars.add(v); + } + } + } + imClass.getFields().addAll(typeConstraintVars); + } private void addSuperClasses() { @@ -124,8 +140,8 @@ private void createDestroyMethod(List subClasses) { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) - .collect(Collectors.toCollection(JassIm::ImTypeArguments)); + .map(tv -> ImTypeArgument(JassIm.ImTypeVarRef(tv))) + .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } @@ -167,7 +183,14 @@ private boolean hasOwnDestroy(ModuleInstanciation mi) { private void createOnDestroyMethod() { OnDestroyDef onDestroy = classDef.getOnDestroy(); ImFunction f = translator.getFuncFor(onDestroy); - addOnDestroyActions(f, f.getBody(), classDef, translator.getThisVar(onDestroy)); + ImVar thisVar = translator.getThisVar(onDestroy); + addOnDestroyActions(f, f.getBody(), classDef, thisVar); + // destroy type + for (ImVar tc : typeConstraintVars) { + ImClassType t = (ImClassType) tc.getType(); + f.getBody().add(JassIm.ImDealloc(onDestroy, t, + JassIm.ImMemberAccess(onDestroy, JassIm.ImVarAccess(thisVar), JassIm.ImTypeArguments(), tc, JassIm.ImExprs()))); + } } private void addOnDestroyActions(ImFunction f, List addTo, ClassOrModuleInstanciation c, ImVar thisVar) { @@ -331,7 +354,7 @@ private WurstTypeClass getExtendedClassType(ClassDef subC) { private ImFunction createStaticCallFunc(FuncDef funcDef) { ImFunction f = translator.getFuncFor(funcDef); f.getBody().addAll(translator.translateStatements(f, funcDef.getBody())); - // TODO add return for abstract function + // add dummy return for abstract function if (funcDef.attrIsAbstract() && !(funcDef.attrReturnType() instanceof WurstTypeVoid)) { f.getBody().add(ImReturn(funcDef, funcDef.attrReturnType().getDefaultValue(translator))); } @@ -344,21 +367,29 @@ private void translateConstructor(ConstructorDef constr) { createConstructFunc(constr); } - + /** + * The new function contains the generic part: + * 1. allocating memory + * 2. calling the construct function + * 3. return a reference to the new instance + */ private void createNewFunc(ConstructorDef constr) { ConstructorDef trace = constr; ImFunction f = translator.getConstructNewFunc(constr); - Map varReplacements = Maps.newLinkedHashMap(); for (WParameter p : constr.getParameters()) { - ImVar imP = ImVar(p, p.attrTyp().imTranslateType(translator), p.getName(), false); - varReplacements.put(translator.getVarFor(p), imP); + ImVar imP = ImVar(p, p.attrTyp().imTranslateType(translator), p.getName(), Collections.emptyList()); f.getParameters().add(imP); } + // add type constraint parameters: + List typeConstraintParams = this.typeConstraintVars.stream() + .map(ImVar::copy) + .collect(Collectors.toList()); + f.getParameters().addAll(typeConstraintParams); + - ImVar thisVar = JassIm.ImVar(constr, imClassType(), "this", false); - varReplacements.put(translator.getThisVar(constr), thisVar); + ImVar thisVar = ImVar(constr, imClassType(), "this", Collections.emptyList()); f.getLocals().add(thisVar); // allocate class @@ -372,7 +403,7 @@ private void createNewFunc(ConstructorDef constr) { } ImTypeArguments typeArgs = ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArgs.add(ImTypeArgument(JassIm.ImTypeVarRef(tv))); } f.getBody().add(ImFunctionCall(trace, constrFunc, typeArgs, arguments, false, CallType.NORMAL)); @@ -383,10 +414,31 @@ private void createNewFunc(ConstructorDef constr) { } + /** + * The construct function contains the class-specific code: + * - calls super constructor (if there is a super-class) + * - call function to init class variables + * - custom user defined constructor code + */ private void createConstructFunc(ConstructorDef constr) { ConstructorDef trace = constr; ImFunction f = translator.getConstructFunc(constr); ImVar thisVar = translator.getThisVar(constr); + + // add type constraint parameters: + List typeConstraintParams = this.typeConstraintVars.stream() + .map(ImVar::copy) + .collect(Collectors.toList()); + f.getParameters().addAll(typeConstraintParams); + + // set typeConstraintParams + for (Pair p : Utils.zip(typeConstraintVars, typeConstraintParams)) { + ImVar pField = p.getA(); + ImVar pParam = p.getB(); + f.getBody().add(ImSet(trace, ImMemberAccess(trace, ImVarAccess(thisVar), JassIm.ImTypeArguments(), pField, JassIm.ImExprs()), + JassIm.ImVarAccess(pParam))); + } + ConstructorDef superConstr = constr.attrSuperConstructor(); if (superConstr != null) { // call super constructor @@ -404,15 +456,22 @@ private void createConstructFunc(ConstructorDef constr) { for (WurstTypeBoundTypeParam bt : extendedTypeC.getTypeParameters()) { if (bt.isTemplateTypeParameter()) { typeArgs.add(bt.imTranslateToTypeArgument(translator)); + // add super-constructor type constraint parameters: + for (TypeClassInstance instance : bt.getInstances()) { + arguments.add(instance.translate(trace, thisVar, translator)); + } } } } + f.getBody().add(ImFunctionCall(trace, superConstrFunc, typeArgs, arguments, false, CallType.NORMAL)); } + + // call classInitFunc: ImTypeArguments typeArguments = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArguments.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArguments.add(ImTypeArgument(JassIm.ImTypeVarRef(tv))); } f.getBody().add(ImFunctionCall(trace, classInitFunc, typeArguments, JassIm.ImExprs(JassIm.ImVarAccess(thisVar)), false, CallType.NORMAL)); // constructor user code @@ -421,7 +480,7 @@ private void createConstructFunc(ConstructorDef constr) { private void translateClassInitFunc() { ClassDef trace = classDef; - ImVar thisVar = JassIm.ImVar(trace, imClassType(), "this", false); + ImVar thisVar = ImVar(trace, imClassType(), "this", Collections.emptyList()); classInitFunc = JassIm.ImFunction(classDef, translator.getNameFor(classDef) + "_init", ImTypeVars(), ImVars(thisVar), ImVoid(), ImVars(), ImStmts(), Collections.emptyList()); imClass.getFunctions().add(classInitFunc); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java new file mode 100644 index 000000000..fb97208c4 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClassesOptimizer.java @@ -0,0 +1,149 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import de.peeeq.wurstscript.WurstOperator; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.types.TypesHelper; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * optimizes classes, dynamic dispatch, etc. + * before running EliminateClasses + * + * Precondition: Generics eliminated + */ +public class ClassesOptimizer { + public static void optimizeProg(ImTranslator tr) { + optimizeStatelessClasses(tr); + + optimizeDispatch(tr); + } + + /** + * Search for classes without attributes and replace them with + * a single-instance. + * If the class has no attributes, the only way to distinguish different + * instances of the class is by using equality or casting to int. + * + * This is a necessary optimization for the translation of type classes, + * as otherwise there would be memory leaks. + * A typeclass dict is always a class without attributes. + */ + private static void optimizeStatelessClasses(ImTranslator tr) { + ImProg prog = tr.getImProg(); + + Multimap subClasses = transitiveReflexiveSubclasses(prog); + + Set statelessClasses = prog.getClasses().stream() + .filter(c -> c.getFields().isEmpty() + && subClasses.get(c).size() == 1) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + Multimap allocs = LinkedHashMultimap.create(); + Multimap deAllocs = LinkedHashMultimap.create(); + prog.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImAlloc imAlloc) { + super.visit(imAlloc); + ImClass classDef = imAlloc.getClazz().getClassDef(); + if (statelessClasses.contains(classDef)) { + allocs.put(classDef, imAlloc); + } + } + + @Override + public void visit(ImDealloc deAlloc) { + super.visit(deAlloc); + ImClass classDef = deAlloc.getClazz().getClassDef(); + if (statelessClasses.contains(classDef)) { + deAllocs.put(classDef, deAlloc); + } + } + + @Override + public void visit(ImOperatorCall c) { + super.visit(c); + WurstOperator op = c.getOp(); + if (op == WurstOperator.EQ || op == WurstOperator.NOTEQ) { + for (ImExpr arg : c.getArguments()) { + markNotStateless(arg.attrTyp()); + } + } + } + + private void markNotStateless(ImType attrTyp) { + if (attrTyp instanceof ImClassType) { + ImClassType ct = (ImClassType) attrTyp; + markNotStateless(ct.getClassDef()); + } + } + + private void markNotStateless(ImClass classDef) { + statelessClasses.removeAll(subClasses.get(classDef)); + } + + @Override + public void visit(ImCast c) { + super.visit(c); + if (c.getToType().equalsType(TypesHelper.imInt())) { + markNotStateless(c.getExpr().attrTyp()); + } + } + }); + + for (ImClass c : statelessClasses) { + Collection cAllocs = allocs.get(c); + if (cAllocs.isEmpty()) { + continue; + } + // create singleton variable + ImClassType classType = JassIm.ImClassType(c, JassIm.ImTypeArguments()); + ImVar singletonVar = JassIm.ImVar(c.getTrace(), classType, c.getName() + "_singleton", Collections.emptyList()); + tr.addGlobalWithInitializer(singletonVar, JassIm.ImAlloc(c.getTrace(), classType)); + + // replace all allocations with singleton variable + for (ImAlloc cAlloc : cAllocs) { + cAlloc.replaceBy(JassIm.ImVarAccess(singletonVar)); + } + // remove deAllocs + for (ImDealloc deAlloc : deAllocs.get(c)) { + deAlloc.replaceBy(JassIm.ImNull(JassIm.ImVoid())); + } + + } + } + + /** + * map from class to all subclasses and itself + */ + private static Multimap transitiveReflexiveSubclasses(ImProg prog) { + LinkedHashMultimap res = LinkedHashMultimap.create(); + for (ImClass c : prog.getClasses()) { + addSuperClasses(res, c, c); + } + + return res; + } + + private static void addSuperClasses(LinkedHashMultimap res, ImClass subClass, ImClass superClass) { + res.put(superClass, subClass); + for (ImClassType superSuperClass : superClass.getSuperClasses()) { + addSuperClasses(res, subClass, superSuperClass.getClassDef()); + } + } + + /** + * Analyze the program and find places where the receiver of a method + * is guaranteed to not be null or de-allocated. + * Mark these method calls as unchecked dispatches. + * Unchecked dispatches are later translated to simplified code: If + * there are no sub-methods, they can be directly translated to + * function calls. + */ + private static void optimizeDispatch(ImTranslator tr) { + // TODO implement optimization + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java index 1ee74a06e..f60439bb6 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ClosureTranslator.java @@ -11,10 +11,7 @@ import de.peeeq.wurstscript.translation.imtojass.TypeRewriter; import de.peeeq.wurstscript.types.*; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import static java.util.Collections.singletonList; @@ -45,7 +42,7 @@ public ImExpr translate() { } else { ImClass c = createClass(); ImClassType ct = JassIm.ImClassType(c, getClassTypeArguments()); - ImVar clVar = JassIm.ImVar(e, ct, "clVar", false); + ImVar clVar = JassIm.ImVar(e, ct, "clVar", Collections.emptyList()); f.getLocals().add(clVar); ImStmts stmts = JassIm.ImStmts(); // allocate closure @@ -64,7 +61,7 @@ public ImExpr translate() { private ImTypeArguments getClassTypeArguments() { ImTypeArguments res = JassIm.ImTypeArguments(); for (ImTypeVar typeVar : typeVars.keySet()) { - res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar), Collections.emptyMap())); + res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar))); } return res; } @@ -153,7 +150,7 @@ private ImClass createClass() { tr.getImProg().getFunctions().remove(impl); c.getFunctions().add(impl); ImClassType methodClass = JassIm.ImClassType(c, JassIm.ImTypeArguments()); - ImMethod m = JassIm.ImMethod(e, methodClass, superMethod.getName(), impl, JassIm.ImMethods(), false); + ImMethod m = JassIm.ImMethod(e, methodClass, superMethod.getName(), impl, new ArrayList<>(), false); c.getMethods().add(m); OverrideUtils.addOverrideClosure(tr, superMethod, m, e); @@ -229,9 +226,7 @@ public ImType case_ImTypeVarRef(ImTypeVarRef t) { result.put(oldTypevar, newTypevar); c.getTypeVariables().add(newTypevar); thisType.getTypeArguments().add( - JassIm.ImTypeArgument( - JassIm.ImTypeVarRef(newTypevar), - Collections.emptyMap())); + JassIm.ImTypeArgument(JassIm.ImTypeVarRef(newTypevar))); } return JassIm.ImTypeVarRef(newTypevar); } @@ -271,7 +266,7 @@ private ImVarAccess closureThis() { private ImVar getClosureVarFor(ImVar var) { ImVar v = closureVars.get(var); if (v == null) { - v = JassIm.ImVar(e, var.getType(), var.getName(), false); + v = JassIm.ImVar(e, var.getType(), var.getName(), Collections.emptyList()); c.getFields().add(v); closureVars.put(var, v); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java index 74c9c1301..e9e0504fe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java @@ -46,7 +46,7 @@ private void removeCycle(List funcs) { de.peeeq.wurstscript.ast.Element trace = funcs.get(0).getTrace(); - ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(tr), "funcChoice", false); + ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(tr), "funcChoice", Collections.emptyList()); List flags = Lists.newArrayList(); @@ -212,7 +212,7 @@ private ImVar getTempReturnVar(ImType t) { String typeName = t.translateType(); ImVar r = tempReturnVars.get(typeName); if (r == null) { - r = JassIm.ImVar(t.attrTrace(), t, "tempReturn_" + typeName, false); + r = JassIm.ImVar(t.attrTrace(), t, "tempReturn_" + typeName, Collections.emptyList()); prog.getGlobals().add(r); tempReturnVars.put(typeName, r); } @@ -239,7 +239,7 @@ private void calculateNewParameters(List funcs, } } // otherwise, we have to create a new var: - ImVar newVar = JassIm.ImVar(v.getTrace(), (ImType) v.getType().copy(), v.getName(), false); + ImVar newVar = JassIm.ImVar(v.getTrace(), (ImType) v.getType().copy(), v.getName(), Collections.emptyList()); oldToNewVar.put(v, newVar); newParameters.add(newVar); pos = newParameters.size() + 1; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java index 3a817ce59..48c0d1f98 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java @@ -133,7 +133,7 @@ public static int calculateMaxTypeId(ImProg prog) { @NotNull private ImFunction accessClassManagementVar(String funcName, ImType returnType, ImExpr defaultReturn, Function makeAccess) { Element trace = prog.getTrace(); - ImVar typeId = JassIm.ImVar(trace, TypesHelper.imInt(), "typeId", false); + ImVar typeId = JassIm.ImVar(trace, TypesHelper.imInt(), "typeId", Collections.emptyList()); ImVars parameters = JassIm.ImVars(typeId); ImVars locals = JassIm.ImVars(); Map classId = prog.attrTypeId(); @@ -200,6 +200,11 @@ private ImType eliminateClassTypes(ImType imType) { public ImType case_ImClassType(ImClassType t) { return imInt(); } + + @Override + public ImType case_ImAnyType(ImAnyType t) { + return imInt(); + } }); } @@ -218,8 +223,7 @@ private void eliminateClass(ImClass c) { // for each field, create a global array variable for (ImVar f : c.getFields()) { ImType type = ImHelper.toArray(f.getType()); - ImVar v = JassIm - .ImVar(f.getTrace(), type, f.getName(), false); + ImVar v = JassIm.ImVar(f.getTrace(), type, f.getName(), Collections.emptyList()); prog.getGlobals().add(v); fieldToArray.put(f, v); } @@ -274,7 +278,7 @@ public void createDispatchFunc(ImClass c, ImMethod m) { resultVar = null; } else { resultVar = JassIm.ImVar(df.getTrace(), returnType, m.getName() - + "_result", false); + + "_result", Collections.emptyList()); df.getLocals().add(resultVar); } @@ -564,7 +568,7 @@ private void replaceInstanceof(ImInstanceof e) { ImExpr objTypeIdExpr = objTypeId; if (useTempVar) { // use temporary variable - tempVar = JassIm.ImVar(e.attrTrace(), imInt(), "instanceOfTemp", false); + tempVar = JassIm.ImVar(e.attrTrace(), imInt(), "instanceOfTemp", Collections.emptyList()); f.getLocals().add(tempVar); objTypeIdExpr = JassIm.ImVarAccess(tempVar); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java index 31e93ef0a..fa6c5e066 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -99,13 +99,12 @@ private static ImClassType adaptToSuperclass(ImClassType ct, ImClass owningClass } private static Iterable superTypes(ImClassType ct) { - GenericTypes generics = new GenericTypes(ct.getTypeArguments()); List typeVars = ct.getClassDef().getTypeVariables(); return () -> ct.getClassDef() .getSuperClasses() .stream() - .map(sc -> (ImClassType) transformType(sc, generics, typeVars)) + .map(sc -> (ImClassType) ImAttrType.substituteType(sc, ct.getTypeArguments(), typeVars)) .iterator(); } @@ -144,9 +143,9 @@ private void moveFunctionsOutOfClass(ImClass c) { f.getTypeVariables().addAll(0, newTypeVars); List typeArgs = newTypeVars .stream() - .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap())) + .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta))) .collect(Collectors.toList()); - rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables()); + rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables(), newTypeVars); } } @@ -188,11 +187,11 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) { ImFunction newF = f.copyWithRefs(); specializedFunctions.put(f, generics, newF); prog.getFunctions().add(newF); - newF.getTypeVariables().removeAll(); + List newTypeVars = newF.getTypeVariables().removeAll(); List typeVars = f.getTypeVariables(); newF.setName(f.getName() + "⟪" + generics.makeName() + "⟫"); - rewriteGenerics(newF, generics, typeVars); + rewriteGenerics(newF, generics, typeVars, newTypeVars); collectGenericUsages(newF); return newF; } @@ -253,7 +252,7 @@ private void adaptSubmethods(List oldSubMethods, ImMethod newM) { /** * Replaces all uses of the given typeVars with the type arguments given in parameter generics. */ - private void rewriteGenerics(Element element, GenericTypes generics, List typeVars) { + private void rewriteGenerics(Element element, GenericTypes generics, List typeVars, List newTypeVars) { if (generics.getTypeArguments().size() != typeVars.size()) { throw new RuntimeException("Rewrite generics with wrong sizes\n" + "generics: " + generics + "\n" + @@ -346,11 +345,11 @@ private ImClass specializeClass(ImClass c, GenericTypes generics) { newC.setSuperClasses(new ArrayList<>(newC.getSuperClasses())); specializedClasses.put(c, generics, newC); prog.getClasses().add(newC); - newC.getTypeVariables().removeAll(); + List newTypeVars = newC.getTypeVariables().removeAll(); newC.setName(c.getName() + "⟪" + generics.makeName() + "⟫"); List typeVars = c.getTypeVariables(); - rewriteGenerics(newC, generics, typeVars); + rewriteGenerics(newC, generics, typeVars, newTypeVars); newC.getSuperClasses().replaceAll(this::specializeType); // we don't collect generic usages to avoid infinite loops // in cases like class C { C> x; } @@ -399,7 +398,7 @@ public void visit(ImVar v) { super.visit(v); if (isGenericType(v.getType())) { if (containsTypeVariable(v.getType())) { - throw new CompileError(v, "Var should not have type variables."); + throw new CompileError(v, "Var " + v + " should not have type variables."); } genericsUses.add(new GenericVar(v)); } @@ -434,6 +433,7 @@ public void visit(ImFunction f) { @Override public void visit(ImAlloc f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -441,6 +441,7 @@ public void visit(ImAlloc f) { @Override public void visit(ImDealloc f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -448,6 +449,7 @@ public void visit(ImDealloc f) { @Override public void visit(ImInstanceof f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -455,6 +457,7 @@ public void visit(ImInstanceof f) { @Override public void visit(ImTypeIdOfObj f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -462,6 +465,15 @@ public void visit(ImTypeIdOfObj f) { @Override public void visit(ImTypeIdOfClass f) { + super.visit(f); + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + + @Override + public void visit(ImTypeClassDictValue f) { + super.visit(f); if (isGenericType(f.getClazz())) { genericsUses.add(new GenericClazzUse(f)); } @@ -670,7 +682,9 @@ public ImType case_ImClassType(ImClassType t) { private List specializeTypeArgs(ImTypeArguments typeArgs) { return typeArgs .stream() - .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()), ta.getTypeClassBinding())) + .map(ta -> { + return JassIm.ImTypeArgument(specializeType(ta.getType())); + }) .collect(Collectors.toList()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java index fca20a886..7b895760d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java @@ -182,7 +182,7 @@ public void visit(ImVarArrayAccess va) { for (ImExpr ie : indexes) { if (sideEffects) { // use temp variables if there are side effects - ImVar tempIndex = JassIm.ImVar(ie.attrTrace(), TypesHelper.imInt(), "tempIndex", false); + ImVar tempIndex = JassIm.ImVar(ie.attrTrace(), TypesHelper.imInt(), "tempIndex", Collections.emptyList()); indexExprs.add(JassIm.ImVarAccess(tempIndex)); f.getLocals().add(tempIndex); ie.setParent(null); @@ -392,7 +392,7 @@ private static ImStatementExpr inSet(ImSet imSet, ImFunction f) { List tempVars = new ArrayList<>(); // 2) assign right hand side to temporary variables: for (ImExpr expr : right.getExprs()) { - ImVar temp = JassIm.ImVar(expr.attrTrace(), expr.attrTyp(), "tuple_temp", false); + ImVar temp = JassIm.ImVar(expr.attrTrace(), expr.attrTyp(), "tuple_temp", Collections.emptyList()); expr.setParent(null); stmts.add(JassIm.ImSet(expr.attrTrace(), JassIm.ImVarAccess(temp), expr)); tempVars.add(temp); @@ -452,7 +452,7 @@ private static Element inTupleSelection(ImTupleSelection ts, ImTupleExpr tupleEx // TODO maybe this assumption should be validated ... result = extractSideEffect(te, stmts); } else { - ImVar temp = JassIm.ImVar(trace, te.attrTyp(), "tupleSelection", false); + ImVar temp = JassIm.ImVar(trace, te.attrTyp(), "tupleSelection", Collections.emptyList()); f.getLocals().add(temp); stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(temp), te)); result = JassIm.ImVarAccess(temp); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java new file mode 100644 index 000000000..95debbb0f --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTypeClasses.java @@ -0,0 +1,417 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import com.google.common.collect.*; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; +import de.peeeq.wurstscript.jassIm.*; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Eliminates ImTypeClassDictValue from the program. + *

+ * The algorithm works like this: + * Initialize a list of all ImTypeClassDictValue with no dynamic values. + * Maintain this list invariant and while the list is not empty: + *

+ * Look at where the dict is used: + * When used as a function parameter: specialize the function + * When used as a method parameter: specialize the method (including submethods and implementation) + * When assigned to a class field: That must be in a constructor. Specialize the class with all the assignments used in the constructor. + * When used as receiver in a method call: Specialize method (if no submethods, replace by function call) + * No other uses should be possible --> compilation error. + *

+ * Keep Mappings from Function (etc.) and type to specialized version such that every specialization + * is only done once. + *

+ * Replace the call to the generic function with a call to the specialized one. + *

+ * When specializing a parameter to a type: + * - Replace all uses of the parameter with a concrete dict + * - Add the concrete dicts to the work list + */ +public class EliminateTypeClasses { + private final ImTranslator tr; + private final ArrayDeque workList = new ArrayDeque<>(); + private final Table specializedFunctions = HashBasedTable.create(); + private final Table outOfClassFuncs = HashBasedTable.create(); + private final Map typeClassSpecialization = new HashMap<>(); + + public EliminateTypeClasses(ImTranslator tr) { + this.tr = tr; + } + + public static void transform(ImTranslator tr) { + new EliminateTypeClasses(tr).run(); + + + } + + private void run() { + ImProg prog = tr.getImProg(); + + addTypeClassDictValuesToWorkList(prog); + + while (!workList.isEmpty()) { + ImTypeClassDictValue dictV = workList.removeFirst(); + doSpecialize(dictV); + + } + } + + private void addTypeClassDictValuesToWorkList(Element e) { + e.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImTypeClassDictValue dv) { + // no super visit + if (!dynamicArgsStream(dv).findAny().isPresent()) { + workList.add(dv); + } + } + }); + } + + private void doSpecialize(ImTypeClassDictValue dictV) { + Element parent = dictV.getParent(); + if (parent == null) { + return; + } else if (parent instanceof ImExprs) { + Element parent2 = parent.getParent(); + if (parent2 instanceof ImFunctionCall) { + ImFunctionCall fc = (ImFunctionCall) parent2; + int index = fc.getArguments().indexOf(dictV); + ImFunction specializedFunc = getSpecializeFunc(fc.getFunc(), key(dictV, index)); + fc.getArguments().remove(index); + fc.setFunc(specializedFunc); + return; + } + } else if (parent instanceof ImMethodCall) { + ImMethodCall mc = (ImMethodCall) parent; + if (mc.getParent() == null) { + return; + } + assert mc.getReceiver() == dictV; + + ImMethod m = findMostConcreteMethod(mc.getMethod(), dictV.getClazz()); + + // allow to move + mc.getTypeArguments().setParent(null); + mc.getArguments().setParent(null); + + if (!m.getSubMethods().isEmpty()) { + throw new CompileError(dictV.getTrace(), + "Could not specialize method call: " + mc + " to " + m); + } + mc.replaceBy(JassIm.ImFunctionCall( + mc.getTrace(), + getOutOfClassFunc(m.getImplementation(), key(dictV)), + mc.getTypeArguments(), + mc.getArguments(), + false, + CallType.NORMAL + )); + + return; + } else if (parent instanceof ImMemberAccess) { + ImMemberAccess ma = (ImMemberAccess) parent; + ImVar field = ma.getVar(); + int index = ((ImVars) field.getParent()).indexOf(field); + ImExpr tc = dictV.getArguments().get(index); + ImExpr copy = tc.copyWithRefs(); + ma.replaceBy(copy); + if (copy instanceof ImTypeClassDictValue) { + workList.add((ImTypeClassDictValue) copy); + } + return; + } + + // if used in other places (e.g. class parameters), don't monomorphize and + // just use the dynamic version here: + dictV.replaceBy(typeClassAlloc(dictV)); + } + + /** + * Dynamically allocate an instance of this type-class-instance. + */ + private Element typeClassAlloc(ImTypeClassDictValue dictV) { + ImClass clazz = getSpecializedTypeClassDict(key(dictV)); + return JassIm.ImAlloc(dictV.getTrace(), JassIm.ImClassType(clazz, JassIm.ImTypeArguments())); + } + + private ImClass getSpecializedTypeClassDict(TypeClassInstanceKey key) { + + return typeClassSpecialization.computeIfAbsent(key, k -> { + if (key.args.isEmpty()) { + return key.base; + } else { + return specializeClass(key); + } + }); + } + + private ImClass specializeClass(TypeClassInstanceKey key) { + ImProg imProg = tr.getImProg(); + ImClass base = key.base; + // create new subclass + ImClass sub = JassIm.ImClass( + base.getTrace(), + base.getName() + key.args, + JassIm.ImTypeVars(), + JassIm.ImVars(), + JassIm.ImMethods(), + JassIm.ImFunctions(), + Collections.singletonList(JassIm.ImClassType(base, JassIm.ImTypeArguments())) + ); + imProg.getClasses().add(sub); + + + // specialize methods using the fields + final ImVars baseFields = base.getFields(); + + + List toSpecialize = imProg.getMethods() + .stream() + .filter(m -> m.getMethodClass().getClassDef() == base + && !collectFieldAccesses(m.getImplementation(), baseFields).isEmpty()) + .collect(Collectors.toList()); + + + // if one of the fields is used, create a new submethod: + for (ImMethod m : toSpecialize) { + + ImFunction impl = m.getImplementation(); + + + ImFunction newImpl = impl.copyWithRefs(); + List fieldAccesses = collectFieldAccesses(newImpl, baseFields); + for (ImMemberAccess fa : fieldAccesses) { + int index = baseFields.indexOf(fa.getVar()); + TypeClassInstanceKey tc = key.args.get(index); + ImTypeClassDictValue dictValue = tc.makeDictValue(fa.getTrace()); + fa.replaceBy(dictValue); + workList.add(dictValue); + } + + ImMethod newM = JassIm.ImMethod( + m.getTrace(), + JassIm.ImClassType(sub, JassIm.ImTypeArguments()), + m.getName(), + newImpl, + new ArrayList<>(), + false + ); + + m.getSubMethods().add(newM); + + addTypeClassDictValuesToWorkList(newImpl); + imProg.getFunctions().add(newImpl); + imProg.getMethods().add(newM); + } + + return sub; + } + + @NotNull + private List collectFieldAccesses(ImFunction impl, ImVars baseFields) { + List fieldAccesses = new ArrayList<>(); + impl.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImMemberAccess e) { + super.visit(e); + if (baseFields.contains(e.getVar())) { + fieldAccesses.add(e); + } + } + }); + return fieldAccesses; + } + + private ImFunction getOutOfClassFunc(ImFunction impl, TypeClassInstanceKey key) { + ImFunction res = outOfClassFuncs.get(impl, key); + if (res != null) { + return res; + } + ImFunction copy = impl.copyWithRefs(); + tr.getImProg().getFunctions().add(copy); + // remove implicit parameter + ImVar thisVar = copy.getParameters().remove(0); + List vars = new ArrayList<>(); + copy.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImVarAccess v) { + + if (v.getVar() == thisVar) { + vars.add(v); + } + } + }); + + for (ImVarAccess va : vars) { + ImTypeClassDictValue newDict = key.makeDictValue(va.attrTrace()); + va.replaceBy(newDict); + workList.add(newDict); + } + + outOfClassFuncs.put(impl, key, copy); + return copy; + } + + private ImMethod findMostConcreteMethod(ImMethod method, ImClassType classType) { + ImMethod result = method; + for (ImMethod sub : method.getSubMethods()) { + if (isSubClass(classType, sub.getMethodClass()) && isSubClass(sub.getMethodClass(), result.getMethodClass())) { + result = sub; + } + } + return result; + } + + private boolean isSubClass(ImClassType sub, ImClassType sup) { + return isSubClass(sub.getClassDef(), sup.getClassDef()); + } + + private boolean isSubClass(ImClass sub, ImClass sup) { + return sub == sup + || sub.getSuperClasses().stream() + .anyMatch(sc -> isSubClass(sc.getClassDef(), sup)); + } + + private ImFunction getSpecializeFunc(ImFunction func, ParameterSpecializationKey key) { + ImFunction res = specializedFunctions.get(func, key); + if (res != null) { + return res; + } + res = specializeFunc(func, key); + specializedFunctions.put(func, key, res); + return res; + } + + private ImFunction specializeFunc(ImFunction func, ParameterSpecializationKey key) { + ImFunction copy = func.copyWithRefs(); + copy.setName(func.getName() + "_" + key); + ImVar removedParam = copy.getParameters().remove(key.index); + Set uses = new HashSet<>(); + copy.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImVarAccess va) { + super.visit(va); + if (va.getVar() == removedParam) { + uses.add(va); + } + } + }); + + for (ImVarAccess use : uses) { + ImTypeClassDictValue newDict = key.key.makeDictValue(use.attrTrace()); + use.replaceBy(newDict); + workList.add(newDict); + } + addTypeClassDictValuesToWorkList(copy); + tr.getImProg().getFunctions().add(copy); + return copy; + } + + + private static Stream dynamicArgsStream(ImTypeClassDictValue dv) { + return dv.getArguments().stream().flatMap(EliminateTypeClasses::dynamicArgsStreamE); + } + + private static Stream dynamicArgsStreamE(ImExpr a) { + if (a instanceof ImVarAccess) { + return Stream.of(((ImVarAccess) a).getVar()); + } else if (a instanceof ImTypeClassDictValue) { + return dynamicArgsStream((ImTypeClassDictValue) a); + } + throw new RuntimeException("invalid TypeClassDictValue " + a + " // " + a.getClass()); + } + + private static List dynamicArgs(ImTypeClassDictValue dv) { + return dynamicArgsStream(dv).collect(Collectors.toList()); + } + + private ParameterSpecializationKey key(ImTypeClassDictValue dictV, int index) { + return new ParameterSpecializationKey(index, + new TypeClassInstanceKey(dictV.getClazz().getClassDef(), + dictV.getArguments().stream() + .map(v -> key((ImTypeClassDictValue) v)) + .collect(Collectors.toList()))); + } + + private TypeClassInstanceKey key(ImTypeClassDictValue dictV) { + return new TypeClassInstanceKey(dictV.getClazz().getClassDef(), + dictV.getArguments().stream() + .map(v -> key((ImTypeClassDictValue) v)) + .collect(Collectors.toList())); + } + + static class ParameterSpecializationKey { + private int index; + private TypeClassInstanceKey key; + + public ParameterSpecializationKey(int index, TypeClassInstanceKey key) { + this.index = index; + this.key = key; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ParameterSpecializationKey that = (ParameterSpecializationKey) o; + return index == that.index && + key.equals(that.key); + } + + @Override + public int hashCode() { + return Objects.hash(index, key); + } + + @Override + public String toString() { + return key + "@" + index; + } + } + + // TODO remove index here and make another class for parameter index + static class TypeClassInstanceKey { + private final ImClass base; + + private final List args; + + TypeClassInstanceKey(ImClass base, List args) { + this.base = base; + this.args = args; + } + + @Override + public String toString() { + return "#" + base + args; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TypeClassInstanceKey that = (TypeClassInstanceKey) o; + return Objects.equals(base, that.base) && + Objects.equals(args, that.args); + } + + @Override + public int hashCode() { + return Objects.hash(base, args); + } + + public ImTypeClassDictValue makeDictValue(de.peeeq.wurstscript.ast.Element trace) { + ImExprs args2 = args.stream() + .map(x -> x.makeDictValue(trace)) + .collect(Collectors.toCollection(JassIm::ImExprs)); + return JassIm.ImTypeClassDictValue(trace, JassIm.ImClassType(base, JassIm.ImTypeArguments()), args2); + } + } + +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java index 34bce881f..c5de91199 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ExprTranslation.java @@ -5,8 +5,10 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.names.OtherLink; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.jassIm.ImClass; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.jassIm.ImExprs; @@ -16,12 +18,11 @@ import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; -import io.vavr.control.Either; import io.vavr.control.Option; +import org.eclipse.jdt.annotation.Nullable; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; import static de.peeeq.wurstscript.jassIm.JassIm.*; @@ -298,7 +299,7 @@ private static ImExpr translateTupleSelection(ImTranslator t, ImFunction f, Expr } else { // if tupleExpr is not an l-value (e.g. foo().x) // store result in intermediate variable first: - ImVar v = ImVar(left.attrTrace(), left.attrTyp(), "temp_tuple", false); + ImVar v = ImVar(left.attrTrace(), left.attrTyp(), "temp_tuple", Collections.emptyList()); f.getLocals().add(v); return JassIm.ImStatementExpr( JassIm.ImStmts( @@ -442,10 +443,11 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu List arguments = Lists.newArrayList(e.getArgs()); Expr leftExpr = null; boolean dynamicDispatch = false; + TypeParamConstraint typeParamDispatchOn = getTypeParamConstraint(e); FunctionDefinition calledFunc = e.attrFuncDef(); - if (e.attrImplicitParameter() instanceof Expr) { + if (typeParamDispatchOn == null && e.attrImplicitParameter() instanceof Expr) { if (isCalledOnDynamicRef(e) && calledFunc instanceof FuncDef) { dynamicDispatch = true; } @@ -500,7 +502,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu if (returnReveiver) { if (leftExpr == null) throw new Error("impossible"); - tempVar = JassIm.ImVar(leftExpr, leftExpr.attrTyp().imTranslateType(t), "receiver", false); + tempVar = ImVar(leftExpr, leftExpr.attrTyp().imTranslateType(t), "receiver", Collections.emptyList()); f.getLocals().add(tempVar); stmts = JassIm.ImStmts(ImSet(e, ImVarAccess(tempVar), receiver)); receiver = JassIm.ImVarAccess(tempVar); @@ -509,9 +511,32 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu ImExpr call; - if (dynamicDispatch) { + if (typeParamDispatchOn != null) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); +// if (receiver != null) { +// imArgs.add(0, receiver); +// } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs, getThisVar(f)); + ImVar typeClassParam = t.getTypeClassParamFor(typeParamDispatchOn); + ImExpr typeClassDict; + if (typeClassParam.getParent().getParent() instanceof ImFunction) { + // parameter + typeClassDict = JassIm.ImVarAccess(typeClassParam); + } else { + // field + ImVar thisVar = t.getThisVarForNode(f, e); + typeClassDict = JassIm.ImMemberAccess(e, + JassIm.ImVarAccess(thisVar), + JassIm.ImTypeArguments(), + typeClassParam, + JassIm.ImExprs()); + } + call = JassIm.ImMethodCall(e, method, typeArguments, typeClassDict, imArgs, false); + } else if (dynamicDispatch) { + ImMethod method = t.getMethodFor((FuncDef) calledFunc); + ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables(), imArgs, getThisVar(f)); call = ImMethodCall(e, method, typeArguments, receiver, imArgs, false); } else { ImFunction calledImFunc = t.getFuncFor(calledFunc); @@ -519,12 +544,11 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu imArgs.add(0, receiver); } ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables()); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables(), imArgs, getThisVar(f)); call = ImFunctionCall(e, calledImFunc, typeArguments, imArgs, false, CallType.NORMAL); } if (returnReveiver) { - if (stmts == null) - throw new Error("impossible"); stmts.add(call); return JassIm.ImStatementExpr(stmts, JassIm.ImVarAccess(tempVar)); } else { @@ -532,6 +556,42 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } } + @org.jetbrains.annotations.Nullable + private static ImVar getThisVar(ImFunction f) { + return f.getParameters().isEmpty() ? null : Utils.getFirst(f.getParameters()); + } + + @Nullable + private static TypeParamConstraint getTypeParamConstraint(FunctionCall e) { + FuncLink funcLink = e.attrFuncLink(); + if (funcLink == null) { + return null; + } + return funcLink.getTypeParamConstraint(); + } + + /** + * For each type parameter constraint adds an argument containing the type class dictionary. + */ + private static void addTypeClassDictArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables, ImExprs imArgs, ImVar thisVar) { + VariableBinding mapping = sig.getMapping(); + for (ImTypeVar tv : typeVariables) { + TypeParamDef tp = tr.getTypeParamDef(tv); + Option to = mapping.get(tp); + if (to.isEmpty()) { + throw new CompileError(location, "Type variable " + tp.getName() + " not bound in mapping."); + } + WurstTypeBoundTypeParam t = to.get(); + if (!t.isTemplateTypeParameter()) { + continue; + } + for (TypeClassInstance instance : t.getInstances()) { + ImExpr arg = instance.translate(location, thisVar, tr); + imArgs.add(arg); + } + } + } + private static ImTypeArguments getFunctionCallTypeArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables) { ImTypeArguments res = ImTypeArguments(); VariableBinding mapping = sig.getMapping(); @@ -545,10 +605,8 @@ private static ImTypeArguments getFunctionCallTypeArguments(ImTranslator tr, Fun if (!t.isTemplateTypeParameter()) { continue; } - ImType type = t.imTranslateType(tr); - // TODO handle constraints - Map> typeClassBinding = new HashMap<>(); - res.add(ImTypeArgument(type, typeClassBinding)); + ImTypeArgument imTypeArgument = t.imTranslateToTypeArgument(tr); + res.add(imTypeArgument); } return res; } @@ -582,7 +640,13 @@ public static ImExpr translateIntern(ExprNewObject e, ImTranslator t, ImFunction WurstTypeClass wurstType = (WurstTypeClass) e.attrTyp(); ImClass imClass = t.getClassFor(wurstType.getClassDef()); ImTypeArguments typeArgs = getFunctionCallTypeArguments(t, sig, e, imClass.getTypeVariables()); - return ImFunctionCall(e, constructorImFunc, typeArgs, translateExprs(e.getArgs(), t, f), false, CallType.NORMAL); + ImExprs args = translateExprs(e.getArgs(), t, f); + + // translate type constraints: + ImTypeVars tps = t.getClassFor(constructorFunc.attrNearestClassDef()).getTypeVariables(); + addTypeClassDictArguments(t, e.attrFunctionSignature(), e, tps, args, getThisVar(f)); + + return ImFunctionCall(e, constructorImFunc, typeArgs, args, false, CallType.NORMAL); } public static ImExprOpt translate(NoExpr e, ImTranslator translator, ImFunction f) { @@ -674,7 +738,7 @@ public static ImExpr translate(ExprIfElse e, ImTranslator t, ImFunction f) { ImExpr ifTrue = e.getIfTrue().imTranslateExpr(t, f); ImExpr ifFalse = e.getIfFalse().imTranslateExpr(t, f); // TODO common super type of both - ImVar res = JassIm.ImVar(e, ifTrue.attrTyp(), "cond_result", false); + ImVar res = ImVar(e, ifTrue.attrTyp(), "cond_result", Collections.emptyList()); f.getLocals().add(res); return JassIm.ImStatementExpr( ImStmts( diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java index 08735dafd..390632698 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Flatten.java @@ -58,10 +58,6 @@ public class Flatten { - public static Result flatten(ImTypeVarDispatch imTypeVarDispatch, ImTranslator translator, ImFunction f) { - throw new RuntimeException("called too early"); - } - public static Result flatten(ImCast imCast, ImTranslator translator, ImFunction f) { Result res = imCast.getExpr().flatten(translator, f); return new Result(res.stmts, ImCast(res.expr, imCast.getToType())); @@ -92,6 +88,11 @@ public static Result flatten(ImAlloc e, ImTranslator translator, ImFunction f) { return new Result(e); } + public static Result flatten(ImTypeClassDictValue e, ImTranslator translator, ImFunction f) { + MultiResult r = flattenExprs(translator, f, e.getArguments()); + return new Result(r.stmts, JassIm.ImTypeClassDictValue(e.getTrace(), e.getClazz(), JassIm.ImExprs(r.exprs))); + } + public static class Result { @@ -320,7 +321,7 @@ public static Result flatten(ImOperatorCall e, ImTranslator t, ImFunction f) { return new Result(left.stmts, JassIm.ImOperatorCall(WurstOperator.AND, ImExprs(left.expr, right.expr))); } else { ArrayList stmts = Lists.newArrayList(left.stmts); - ImVar tempVar = JassIm.ImVar(e.attrTrace(), WurstTypeBool.instance().imTranslateType(t), "andLeft", false); + ImVar tempVar = ImVar(e.attrTrace(), WurstTypeBool.instance().imTranslateType(t), "andLeft", Collections.emptyList()); f.getLocals().add(tempVar); ImStmts thenBlock = JassIm.ImStmts(); // if left is true then check right @@ -340,7 +341,7 @@ public static Result flatten(ImOperatorCall e, ImTranslator t, ImFunction f) { return new Result(left.stmts, JassIm.ImOperatorCall(WurstOperator.OR, ImExprs(left.expr, right.expr))); } else { ArrayList stmts = Lists.newArrayList(left.stmts); - ImVar tempVar = JassIm.ImVar(trace, WurstTypeBool.instance().imTranslateType(t), "andLeft", false); + ImVar tempVar = ImVar(trace, WurstTypeBool.instance().imTranslateType(t), "andLeft", Collections.emptyList()); f.getLocals().add(tempVar); // if left is true then result is ture ImStmts thenBlock = JassIm.ImStmts(ImSet(trace, ImVarAccess(tempVar), JassIm.ImBoolVal(true))); @@ -411,7 +412,7 @@ public static ResultL flattenL(ImTupleSelection e, ImTranslator t, ImFunction f) } else { // in the unlikely event that this is not an l-value (e.g. foo().x) // we create a temporary variable and store the result there - ImVar v = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "tuple_temp", false); + ImVar v = ImVar(e.attrTrace(), r.expr.attrTyp(), "tuple_temp", Collections.emptyList()); f.getLocals().add(v); stmts = new ArrayList<>(r.stmts); stmts.add(JassIm.ImSet(e.attrTrace(), ImVarAccess(v), r.expr)); @@ -486,7 +487,7 @@ private static MultiResult flattenExprs(ImTranslator t, ImFunction f, List= withStmts) { newExprs.add(r.expr); } else { - ImVar tempVar = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", false); + ImVar tempVar = ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", Collections.emptyList()); f.getLocals().add(tempVar); stmts.add(ImSet(e.attrTrace(), ImVarAccess(tempVar), r.expr)); newExprs.add(JassIm.ImVarAccess(tempVar)); @@ -517,7 +518,7 @@ private static MultiResultL flattenExprsL(ImTranslator t, ImFunction f, List= withStmts) { newExprs.add(r.getExpr()); } else { - ImVar tempVar = JassIm.ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", false); + ImVar tempVar = ImVar(e.attrTrace(), r.expr.attrTyp(), "temp", Collections.emptyList()); f.getLocals().add(tempVar); stmts.add(ImSet(e.attrTrace(), ImVarAccess(tempVar), r.expr)); newExprs.add(JassIm.ImVarAccess(tempVar)); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java index 229478285..bf2b689d8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncRefRemover.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.WurstTypeCode; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,10 +49,9 @@ public void visit(ImFuncRef imFuncRef) { g = refs.get(func); } else { // create global variable containing a reference to the function: - g = JassIm.ImVar(fr.attrTrace(), WurstTypeCode.instance().imTranslateType(tr), - "ref_function_" + func.getName(), false); + g = JassIm.ImVar(fr.attrTrace(), WurstTypeCode.instance().imTranslateType(tr), "ref_function_" + func.getName(), Collections.emptyList()); refs.put(func, g); - tr.addGlobalWithInitalizer(g, fr.copy()); + tr.addGlobalWithInitializer(g, fr.copy()); } fr.replaceBy(JassIm.ImVarAccess(g)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java index 47d36b670..ce21b32bf 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/FuncSkeleton.java @@ -5,11 +5,14 @@ import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.TypesHelper; +import java.util.Collections; + public class FuncSkeleton { public static void create(ConstructorDef constr, ImTranslator translator, ImFunction f) { f.setReturnType(TypesHelper.imInt()); - ImHelper.translateParameters(constr.getParameters(), f.getParameters(), translator); + // TODO add type parameters from class + ImHelper.translateParameters(constr.getParameters(), Collections.emptyList(), f.getParameters(), translator); } public static void create(ExtensionFuncDef funcDef, ImTranslator translator, ImFunction f) { @@ -19,7 +22,7 @@ public static void create(ExtensionFuncDef funcDef, ImTranslator translator, ImF ImVar thisVar = translator.getThisVar(funcDef); thisVar.setType(funcDef.getExtendedType().attrTyp().imTranslateType(translator)); f.getParameters().add(thisVar); - ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); + ImHelper.translateParameters(funcDef.getParameters(), funcDef.getTypeParameters(), f.getParameters(), translator); } public static void create(FuncDef funcDef, ImTranslator translator, ImFunction f) { @@ -30,7 +33,7 @@ public static void create(FuncDef funcDef, ImTranslator translator, ImFunction f ImVar thisVar = translator.getThisVar(funcDef); f.getParameters().add(thisVar); } - ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); + ImHelper.translateParameters(funcDef.getParameters(), funcDef.getTypeParameters(), f.getParameters(), translator); } public static void create(InitBlock initBlock, ImTranslator translator, ImFunction f) { @@ -38,7 +41,7 @@ public static void create(InitBlock initBlock, ImTranslator translator, ImFuncti public static void create(NativeFunc funcDef, ImTranslator translator, ImFunction f) { f.setReturnType(funcDef.attrReturnTyp().imTranslateType(translator)); - ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); + ImHelper.translateParameters(funcDef.getParameters(), Collections.emptyList(), f.getParameters(), translator); } public static void create(TupleDef tupleDef, ImTranslator translator, ImFunction f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java index e4c91482a..a5bf928c3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java @@ -38,9 +38,6 @@ public boolean equals(Object o) { if (!t1.getType().equalsType(t2.getType())) { return false; } - if (!t1.getTypeClassBinding().equals(t2.getTypeClassBinding())) { - return false; - } } return true; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java index df2998686..9b8dbb384 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImHelper.java @@ -1,9 +1,9 @@ package de.peeeq.wurstscript.translation.imtranslation; -import de.peeeq.wurstscript.ast.WParameter; -import de.peeeq.wurstscript.ast.WParameters; +import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.jassIm.Element; import de.peeeq.wurstscript.utils.Constants; import java.util.ArrayList; @@ -12,10 +12,18 @@ public class ImHelper { - static void translateParameters(WParameters params, ImVars result, ImTranslator t) { + static void translateParameters(WParameters params, List typeParameters, ImVars result, ImTranslator t) { for (WParameter p : params) { result.add(t.getVarFor(p)); } + for (TypeParamDef tp : typeParameters) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList cList = (TypeParamConstraintList) tp.getTypeParamConstraints(); + for (TypeParamConstraint c : cList) { + result.add(t.getTypeClassParamFor(c)); + } + } + } } public static ImType toArray(ImType t) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java index def61ce19..2e30c20f1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImPrinter.java @@ -354,6 +354,11 @@ public static void print(ImStmts ss, Appendable sb, int indent) { public static void print(ImVar v, Appendable sb, int indent) { + for (VarFlag varFlag : v.getVarFlags()) { + append(sb, "@"); + append(sb, varFlag); + append(sb, " "); + } v.getType().print(sb, indent); append(sb, " "); append(sb, v.getName()); @@ -501,14 +506,6 @@ public static void print(ImVarargLoop e, Appendable sb, int indent) { } - public static void print(ImTypeVarDispatch e, Appendable sb, int indent) { - append(sb, "<"); - append(sb, e.getTypeVariable().getName()); - append(sb, ">."); - append(sb, e.getTypeClassFunc().getName()); - printArgumentList(sb, indent, e.getArguments()); - } - public static void print(ImTypeVarRef e, Appendable sb, int indent) { append(sb, e.getTypeVariable().getName()); append(sb, smallHash(e.getTypeVariable())); @@ -551,9 +548,6 @@ public static String asString(List s) { .collect(Collectors.joining(", ")) + "]"; } - public static String asString(ImTypeClassFunc s) { - return s.getName() + smallHash(s); - } public static String asString(ImClass s) { return s.getName() + smallHash(s); @@ -564,7 +558,7 @@ public static String asString(ImMethod s) { } public static String asString(ImTypeArgument s) { - return s.getType() + "" + s.getTypeClassBinding(); + return s.getType() + ""; } public static void print(ImCast e, Appendable sb, int indent) { @@ -578,4 +572,10 @@ public static void print(ImCast e, Appendable sb, int indent) { public static void print(ImAnyType at, Appendable sb, int indent) { append(sb, "any"); } + + public static void print(ImTypeClassDictValue e, Appendable sb, int indent) { + append(sb, "Dict#"); + e.getClazz().print(sb, indent); + printArgumentList(sb, indent, e.getArguments()); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java index d59757a69..536984a8c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java @@ -52,7 +52,9 @@ import static de.peeeq.wurstscript.jassIm.JassIm.*; import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*; +import static de.peeeq.wurstscript.types.TypesHelper.imInt; import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath; +import static de.peeeq.wurstscript.utils.Utils.emptyList; public class ImTranslator { @@ -93,7 +95,7 @@ public class ImTranslator { private boolean isUnitTestMode; - private ImVar lastInitFunc = JassIm.ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(this), "lastInitFunc", false); + private ImVar lastInitFunc = ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(this), "lastInitFunc", Collections.emptyList()); private int compiletimeOrderCounter = 1; private final Map compiletimeFlags = new HashMap<>(); @@ -107,7 +109,7 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArg this.wurstProg = wurstProg; this.lasttranslatedThing = wurstProg; this.isUnitTestMode = isUnitTestMode; - imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new LinkedHashMap<>()); + imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), new LinkedHashMap<>()); this.runArgs = runArgs; } @@ -119,8 +121,8 @@ public ImProg translateProg() { try { globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(getGlobalInitFunc()); - debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", - false)), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); + debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", Collections.emptyList())), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); + imProg.getFunctions().add(debugPrintFunction); calculateCompiletimeOrder(); @@ -146,8 +148,8 @@ public ImProg translateProg() { } catch (Throwable t) { WLogger.severe(t); throw new RuntimeException("There was a Wurst bug in the translation of " + Utils.printElementWithSource(lasttranslatedThing) + ": " + t - .getMessage() + - "\nPlease open a ticket with source code and the error log.", t); + .getMessage() + + "\nPlease open a ticket with source code and the error log.", t); } } @@ -259,7 +261,7 @@ private void removeDuplicateNatives(ImProg imProg) { ImFunction existing = natives.get(f.getName()); if (!compatibleTypes(f, existing)) { throw new CompileError(f, "Native function definition conflicts with other native function defined in " + - existing.attrTrace().attrErrorPos()); + existing.attrTrace().attrErrorPos()); } // remove duplicate it.remove(); @@ -344,13 +346,13 @@ private void finishInitFunctions() { ImFunction native_DestroyTrigger = getNativeFunc("DestroyTrigger"); if (native_DestroyTrigger != null) { getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, ImTypeArguments(), - JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); + JassIm.ImExprs(ImVarAccess(initTrigVar)), false, CallType.NORMAL)); } } @NotNull private ImVar prepareTrigger() { - ImVar initTrigVar = JassIm.ImVar(emptyTrace, JassIm.ImSimpleType("trigger"), "initTrig", false); + ImVar initTrigVar = ImVar(emptyTrace, JassIm.ImSimpleType("trigger"), "initTrig", Collections.emptyList()); getMainFunc().getLocals().add(initTrigVar); // initTrigVar = CreateTrigger() @@ -367,7 +369,7 @@ private ImFunction getNativeFunc(String funcName) { if (wurstFunc.isEmpty()) { return null; } - return getFuncFor((TranslatedToImFunction) Utils.getFirst(wurstFunc).getDef()); + return getFuncFor(Utils.getFirst(wurstFunc).getDef()); } private void callInitFunc(Set calledInitializers, WPackage p, ImVar initTrigVar) { @@ -406,11 +408,11 @@ private boolean createInitFuncCall(WPackage p, ImVar initTrigVar, ImFunction ini ImFunction native_GetLocalPlayer = getNativeFunc("GetLocalPlayer"); if (native_ClearTrigger == null - || native_TriggerAddCondition == null - || native_Condition == null - || native_TriggerEvaluate == null - || native_DisplayTimedTextToPlayer == null - || native_GetLocalPlayer == null + || native_TriggerAddCondition == null + || native_Condition == null + || native_TriggerEvaluate == null + || native_DisplayTimedTextToPlayer == null + || native_GetLocalPlayer == null ) { return false; } @@ -426,27 +428,27 @@ public void visit(ImReturn imReturn) { } }); de.peeeq.wurstscript.ast.Element trace = initFunc.getTrace(); - initFunc.getBody().add(JassIm.ImReturn(trace, JassIm.ImBoolVal(true))); + initFunc.getBody().add(ImReturn(trace, JassIm.ImBoolVal(true))); // TriggerAddCondition(initTrigVar, Condition(function myInit)) mainBody.add(ImFunctionCall(trace, native_TriggerAddCondition, ImTypeArguments(), JassIm.ImExprs( - JassIm.ImVarAccess(initTrigVar), - ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs( - JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL) + ImVarAccess(initTrigVar), + ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs( + JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL) ), true, CallType.NORMAL)); // if not TriggerEvaluate(initTrigVar) ... mainBody.add(JassIm.ImIf(trace, - JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs( - ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL) - )), - // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package") - JassIm.ImStmts( - imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + ".")) - ), - // else: - JassIm.ImStmts())); - mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); + JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs( + ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(ImVarAccess(initTrigVar)), false, CallType.NORMAL) + )), + // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package") + ImStmts( + imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + ".")) + ), + // else: + ImStmts())); + mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(ImVarAccess(initTrigVar)), false, CallType.NORMAL)); return true; } @@ -491,7 +493,7 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni if (initialExpr instanceof Expr) { Expr expr = (Expr) initialExpr; ImExpr translated = expr.imTranslateExpr(this, f); - if (!v.getIsBJ()) { + if (!v.isBJ()) { // add init statement for non-bj vars // bj-vars are already initalized by blizzard f.getBody().add(ImSet(trace, ImVarAccess(v), translated)); @@ -500,21 +502,35 @@ public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarIni } else if (initialExpr instanceof ArrayInitializer) { ArrayInitializer arInit = (ArrayInitializer) initialExpr; List translatedExprs = arInit.getValues().stream() - .map(expr -> expr.imTranslateExpr(this, f)) - .collect(Collectors.toList()); + .map(expr -> expr.imTranslateExpr(this, f)) + .collect(Collectors.toList()); for (int i = 0; i < arInit.getValues().size(); i++) { ImExpr translated = translatedExprs.get(i); - f.getBody().add(ImSet(trace, ImVarArrayAccess(trace, v, ImExprs((ImExpr) JassIm.ImIntVal(i))), translated)); + f.getBody().add(ImSet(trace, ImVarArrayAccess(trace, v, ImExprs(JassIm.ImIntVal(i))), translated)); } // add list of init-values to translatedExprs imProg.getGlobalInits().put(v, translatedExprs); } } - public void addGlobalWithInitalizer(ImVar g, ImExpr initial) { + public void addGlobalWithInitializer(ImVar g, ImExpr initial) { imProg.getGlobals().add(g); getGlobalInitFunc().getBody().add(ImSet(g.getTrace(), ImVarAccess(g), initial)); - imProg.getGlobalInits().put(g, Collections.singletonList((ImExpr) initial.copy())); + imProg.getGlobalInits().put(g, Collections.singletonList(initial.copy())); + } + + public void addGlobalWithInitializerFront(ImVar g, ImExpr initial) { + imProg.getGlobals().add(g); + imProg.getGlobalInits().put(g, Collections.singletonList(initial.copy())); + ImStmts body = getGlobalInitFunc().getBody(); + ImStatementExpr init; + if (body.isEmpty() || !(body.get(0) instanceof ImStatementExpr)) { + init = JassIm.ImStatementExpr(ImStmts(), JassIm.ImNull(ImVoid())); + body.add(0, init); + } else { + init = (ImStatementExpr) body.get(0); + } + init.getStatements().add(ImSet(g.getTrace(), ImVarAccess(g), initial)); } @@ -536,7 +552,7 @@ public ImExpr getDefaultValueForJassType(ImType type) { @Override public ImFunction initFor(StructureDef classDef) { - ImVars params = ImVars(JassIm.ImVar(classDef, selfType(classDef), "this", false)); + ImVars params = ImVars(ImVar(classDef, selfTypeS(classDef), "this", Collections.emptyList())); ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); addFunction(f, classDef); @@ -549,8 +565,8 @@ public ImFunction initFor(StructureDef classDef) { @Override public ImMethod initFor(StructureDef classDef) { ImFunction impl = destroyFunc.getFor(classDef); - ImMethod m = JassIm.ImMethod(classDef, selfType(classDef), "destroy" + classDef.getName(), - impl, Lists.newArrayList(), false); + ImMethod m = JassIm.ImMethod(classDef, selfTypeS(classDef), "destroy" + classDef.getName(), + impl, Lists.newArrayList(), false); return m; } }; @@ -600,18 +616,23 @@ public ImType case_ExtensionFuncDef(ExtensionFuncDef f) { } private ImClassType selfType(FuncDef f) { - return selfType(f.attrNearestClassOrInterface()); + return selfType(f.attrNearestClassOrInterfaceOrInstance()); + } + + public ImClassType selfType(ClassOrInterfaceOrInstance classDef) { + ImClass imClass = getClassFor(classDef.attrNearestClassOrInterfaceOrInstance()); + return selfType(imClass); } - public ImClassType selfType(StructureDef classDef) { - ImClass imClass = getClassFor(classDef.attrNearestClassOrInterface()); + public ImClassType selfTypeS(StructureDef classDef) { + ImClass imClass = getClassFor(classDef.attrNearestClassOrInterfaceOrInstance()); return selfType(imClass); } public ImClassType selfType(ImClass imClass) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (ImTypeVar tv : imClass.getTypeVariables()) { - typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + typeArgs.add(ImTypeArgument(JassIm.ImTypeVarRef(tv))); } return JassIm.ImClassType(imClass, typeArgs); } @@ -621,7 +642,7 @@ public ImClassType selfType(ImClass imClass) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), imInt(), JassIm.ImVars(), ImStmts(), Collections.emptyList()); } }; @@ -631,7 +652,7 @@ public ImFunction initFor(ImClass c) { @Override public ImFunction initFor(ImClass c) { - return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(JassIm.ImVar(c.getTrace(), TypesHelper.imInt(), "obj", false)), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(ImVar(c.getTrace(), imInt(), "obj", Collections.emptyList())), TypesHelper.imVoid(), JassIm.ImVars(), ImStmts(), Collections.emptyList()); } }; @@ -648,6 +669,20 @@ public ImTypeVar initFor(TypeParamDef a) { } }; + // type class parameter for type parameters + private final GetAForB constraint = new GetAForB() { + @Override + public ImVar initFor(TypeParamConstraint a) { + ImType t = a.attrConstraintTyp().imTranslateType(ImTranslator.this); + return ImVar(a, t, Utils.printElement(a), emptyList()); + } + + }; + + public ImVar getConstraintFor(TypeParamConstraint c) { + return constraint.getFor(c); + } + public ImFunction getFuncFor(TranslatedToImFunction funcDef) { if (functionMap.containsKey(funcDef)) { @@ -709,6 +744,7 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) { return f; } + private ImClass getClassForFunc(TranslatedToImFunction funcDef) { if (funcDef == null) { return null; @@ -722,7 +758,11 @@ public ImClass case_TupleDef(TupleDef tupleDef) { @Override public ImClass case_FuncDef(FuncDef funcDef) { if (funcDef.attrIsDynamicClassMember()) { - return getClassFor(funcDef.attrNearestClassOrInterface()); + return getClassFor(funcDef.attrNearestClassOrInterfaceOrInstance()); + } + @Nullable WScope nearestScope = funcDef.getParent().attrNearestScope(); + if (nearestScope instanceof InstanceDecl) { + return getClassFor((InstanceDecl) nearestScope); } return null; } @@ -775,7 +815,7 @@ private void handleTypeParameters(TypeParamDefs tps) { } private void handleTypeParameter(TypeParamDef tp) { - if (tp.getTypeParamConstraints() instanceof TypeExprList) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { typeVars.add(typeVariable.getFor(tp)); } } @@ -895,7 +935,7 @@ public ImVar getThisVar(TranslatedToImFunction f) { if (thisVarMap.containsKey(f)) { return thisVarMap.get(f); } - ImVar v = JassIm.ImVar(f, selfType(f), "this", false); + ImVar v = ImVar(f, selfType(f), "this", Collections.emptyList()); thisVarMap.put(f, v); return v; } @@ -908,7 +948,7 @@ public ImVar getThisVar(ImFunction f, ExprSuper e) { return getThisVarForNode(f, e); } - private ImVar getThisVarForNode(ImFunction f, de.peeeq.wurstscript.ast.Element node1) { + ImVar getThisVarForNode(ImFunction f, de.peeeq.wurstscript.ast.Element node1) { de.peeeq.wurstscript.ast.Element node = node1; while (node != null) { if (node instanceof TranslatedToImFunction && !(node instanceof ExprClosure)) { @@ -944,7 +984,7 @@ public ImVar getVarFor(VarDef varDef) { name = getNameFor(varDef.attrNearestNamedScope()) + "_" + name; } boolean isBj = isBJ(varDef.getSource()); - v = JassIm.ImVar(varDef, type, name, isBj); + v = ImVar(varDef, type, name, isBj ? Collections.singletonList(VarFlag.BJ) : Collections.emptyList()); varMap.put(varDef, v); } return v; @@ -1281,6 +1321,119 @@ public boolean isLuaTarget() { } + private Map typeClassParamFor = new LinkedHashMap<>(); + + public ImVar getTypeClassParamFor(TypeParamConstraint tc) { + ImVar v = typeClassParamFor.get(tc); + if (v == null) { + TypeParamDef tp = tc.parentTypeParam(); + WurstTypeInterface wti = (WurstTypeInterface) tc.attrConstraintTyp(); + ImClassType t = wti.imTranslateType(this); + int i = ((TypeParamConstraintList) tp.getTypeParamConstraints()).indexOf(tc); + v = JassIm.ImVar(tc, t, "typeClassDict_" + tp.getName() + ((i > 0) ? i : ""), Collections.singletonList(VarFlag.SPECIALIZE)); + typeClassParamFor.put(tc, v); + } + return v; + } + + private ImClass genericObjectTypeClassInstance; + + public ImClass getGenericObjectTypeClassInstance() { + if (genericObjectTypeClassInstance == null) { + ImMethods methods = JassIm.ImMethods(); + ImFunctions functions = JassIm.ImFunctions(); + de.peeeq.wurstscript.ast.Element t = wurstProg; + +// ImTypeVar typeParam = JassIm.ImTypeVar("T"); + ImClass res = JassIm.ImClass( + t, + "GenericObjectTypeClass", + JassIm.ImTypeVars(), + JassIm.ImVars(), + methods, + functions, + new ArrayList<>() + ); + + // add toIndex function: + ImVar thiz = JassIm.ImVar(t, classType(res), "this", emptyList()); + ImVar p = JassIm.ImVar(t, ImAnyType(), "x", emptyList()); + ImFunction toIndex = JassIm.ImFunction(t, + "toIndex", + JassIm.ImTypeVars(), + JassIm.ImVars(thiz, p), + imInt(), + JassIm.ImVars(), + ImStmts( + ImReturn(t, ImCast(ImVarAccess(p), imInt())) + ), + emptyList()); + functions.add(toIndex); + methods.add(JassIm.ImMethod(t, + classType(res), + "toIndex", + toIndex, + new ArrayList<>(), + false)); + + // add fromIndex function: + thiz = JassIm.ImVar(t, classType(res), "this", emptyList()); + p = JassIm.ImVar(t, imInt(), "x", emptyList()); + ImFunction fromIndex = JassIm.ImFunction(t, + "fromIndex", + JassIm.ImTypeVars(), + JassIm.ImVars(thiz, p), + classType(res), + JassIm.ImVars(), + ImStmts( + ImReturn(t, ImCast(ImVarAccess(p), classType(res))) + ), + emptyList()); + functions.add(fromIndex); + methods.add(JassIm.ImMethod(t, + classType(res), + "fromIndex", + fromIndex, + new ArrayList<>(), + false)); + + // add defaultValue function: + thiz = JassIm.ImVar(t, classType(res), "this", emptyList()); + ImFunction defaultValue = JassIm.ImFunction(t, + "defaultValue", + JassIm.ImTypeVars(), + JassIm.ImVars(thiz), + classType(res), + JassIm.ImVars(), + ImStmts( + ImReturn(t, ImNull(classType(res))) + ), + new ArrayList<>()); + functions.add(defaultValue); + methods.add(JassIm.ImMethod(t, + classType(res), + "defaultValue", + defaultValue, + new ArrayList<>(), + false)); + + imProg.getClasses().add(res); + + genericObjectTypeClassInstance = res; + } + return genericObjectTypeClassInstance; + } + + @NotNull + private ImClassType classType(ImClass res) { + ImTypeArguments args = ImTypeArguments(); + for (ImTypeVar t : res.getTypeVariables()) { + args.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(t))); + } + return JassIm.ImClassType(res, args); + } + + interface VarsForTupleResult { default Iterable allValues() { @@ -1386,7 +1539,7 @@ public VarsForTupleResult case_ImArrayType(ImArrayType at) { return new TupleResult(ts.build()); } // otherwise just create the array variable - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override @@ -1408,7 +1561,7 @@ public VarsForTupleResult case_ImArrayTypeMulti(ImArrayTypeMulti at) { return new TupleResult(ts.build()); } // otherwise just create the array variable - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override @@ -1419,19 +1572,19 @@ public VarsForTupleResult case_ImVoid(ImVoid imVoid) { @Override public VarsForTupleResult case_ImClassType(ImClassType st) { ImType type = typeConstructor.apply(st); - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override public VarsForTupleResult case_ImSimpleType(ImSimpleType st) { ImType type = typeConstructor.apply(st); - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override public VarsForTupleResult case_ImAnyType(ImAnyType at) { ImType type = typeConstructor.apply(at); - return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + return new SingleVarResult(ImVar(tr, type, name, Collections.emptyList())); } @Override @@ -1462,7 +1615,7 @@ private void addVarsForType(List result, String name, ImType type, de.pee } else if (type instanceof ImVoid) { // nothing to add } else { - result.add(JassIm.ImVar(tr, type, name, false)); + result.add(ImVar(tr, type, name, Collections.emptyList())); } } @@ -1505,18 +1658,26 @@ public void assertProperties(Set properties, Element e) { checkVar(((ElementWithVar) e).getVar(), properties); } properties.forEach(p -> p.check(e)); - if (properties.contains(AssertProperty.NOTUPLES)) { - - } - if (properties.contains(AssertProperty.FLAT)) { - - } for (int i = 0; i < e.size(); i++) { Element child = e.get(i); if (child.getParent() == null) { throw new Error("Child " + i + " (" + child + ") of " + e + " not attached to tree"); } - assertProperties(properties, child); + try { + assertProperties(properties, child); + } catch (Throwable t) { + de.peeeq.wurstscript.ast.Element trace = e.attrTrace(); + if (trace instanceof WurstModel || trace instanceof NoExpr) { + throw t; + } + WPos source; + if (t instanceof CompileError) { + source = ((CompileError) t).getSource(); + } else { + source = trace.attrSource(); + } + throw new CompileError(source, "When checking element:\n" + e, CompileError.ErrorType.ERROR, t); + } } } @@ -1564,27 +1725,52 @@ public ImClass getClassForClosure(ExprClosure s) { } - private Map classForStructureDef = Maps.newLinkedHashMap(); + private Map classForStructureDef = Maps.newLinkedHashMap(); - public ImClass getClassFor(ClassOrInterface s) { + public ImClass getClassFor(ClassOrInterfaceOrInstance s) { Preconditions.checkNotNull(s); return classForStructureDef.computeIfAbsent(s, s1 -> { ImTypeVars typeVariables = JassIm.ImTypeVars(); - if (s instanceof AstElementWithTypeParameters) { - for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) { - if (tp.getTypeParamConstraints() instanceof TypeExprList) { - ImTypeVar tv = getTypeVar(tp); - typeVariables.add(tv); - } + for (TypeParamDef tp : s.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + ImTypeVar tv = getTypeVar(tp); + typeVariables.add(tv); } } + String name = s.match(new ClassOrInterfaceOrInstance.Matcher() { + @Override + public String case_InstanceDecl(InstanceDecl i) { + WurstType t = i.getImplementedInterface().attrTyp(); + String name = "TypeClassDict"; + if (t instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) t; + name += "_" + wti.getDef().getName(); + if (!wti.getTypeParameters().isEmpty()) { + WurstTypeBoundTypeParam last = Utils.getLast(wti.getTypeParameters()); + name += "_" + last.getBaseType().getName().replaceAll("[<>]", ""); + } + + + } + return name; + } + + @Override + public String case_ClassDef(ClassDef c) { + return c.getName(); + } - return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + @Override + public String case_InterfaceDef(InterfaceDef i) { + return i.getName(); + } + }); + return JassIm.ImClass(s1, name, typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); }); } - Map methodForFuncDef = Maps.newLinkedHashMap(); + private Map methodForFuncDef = Maps.newLinkedHashMap(); public ImMethod getMethodFor(FuncDef f) { ImMethod m = methodForFuncDef.get(f); @@ -1596,6 +1782,7 @@ public ImMethod getMethodFor(FuncDef f) { return m; } + public ClassManagementVars getClassManagementVarsFor(ImClass c) { return getClassManagementVars().get(c); } @@ -1645,14 +1832,14 @@ public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr mes } private ImFunction makeDefaultErrorFunc() { - ImVar msgVar = JassIm.ImVar(emptyTrace, TypesHelper.imString(), "msg", false); + ImVar msgVar = ImVar(emptyTrace, TypesHelper.imString(), "msg", Collections.emptyList()); ImVars parameters = JassIm.ImVars(msgVar); ImType returnType = JassIm.ImVoid(); ImVars locals = JassIm.ImVars(); - ImStmts body = JassIm.ImStmts(); + ImStmts body = ImStmts(); // print message: - body.add(ImFunctionCall(emptyTrace, getDebugPrintFunction(), ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(msgVar)), false, CallType.NORMAL)); + body.add(ImFunctionCall(emptyTrace, getDebugPrintFunction(), ImTypeArguments(), JassIm.ImExprs(ImVarAccess(msgVar)), false, CallType.NORMAL)); // TODO divide by zero to crash thread: @@ -1667,7 +1854,9 @@ private ImFunction makeDefaultErrorFunc() { List flags = Lists.newArrayList(); - return ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); + ImFunction result = ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); + imProg.getFunctions().add(result); + return result; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java index e9dfef0f6..3e33bf590 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/InterfaceTranslator.java @@ -12,7 +12,6 @@ import de.peeeq.wurstscript.types.WurstTypeInterface; import de.peeeq.wurstscript.types.WurstTypeNamedScope; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -67,7 +66,7 @@ public void addDestroyMethod() { private ImClassType imClassType() { ImTypeArguments typeArgs = imClass.getTypeVariables().stream() - .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv))) .collect(Collectors.toCollection(JassIm::ImTypeArguments)); return JassIm.ImClassType(imClass, typeArgs); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java index 0df79de18..69df1f30e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/MultiArrayEliminator.java @@ -8,6 +8,7 @@ import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Utils; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,7 +47,7 @@ public void run() { int size0 = arraySize.get(0); List newArrays = Lists.newArrayList(); for (int i = 0; i < size0; i++) { - ImVar newVar = JassIm.ImVar(v.getTrace(), JassIm.ImArrayType(type.getEntryType()), v.getName() + "_" + i, false); + ImVar newVar = JassIm.ImVar(v.getTrace(), JassIm.ImArrayType(type.getEntryType()), v.getName() + "_" + i, Collections.emptyList()); newArrays.add(newVar); } ImFunction setFunc = generateSetFunc(v, newArrays); @@ -168,9 +169,9 @@ private void replaceVars(Element e, Map oldToNewVar) { private ImFunction generateSetFunc(ImVar aVar, List newArrays) { ImArrayTypeMulti mtype = (ImArrayTypeMulti) aVar.getType(); ImVars locals = JassIm.ImVars(); - ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "instanceId", false); - ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "arrayIndex", false); - ImVar value = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "value", false); + ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "instanceId", Collections.emptyList()); + ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "arrayIndex", Collections.emptyList()); + ImVar value = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "value", Collections.emptyList()); ImFunctionCall error = imError(aVar, "Index out of Bounds"); ImStmts thenBlock = JassIm.ImStmts(error); ImStmts elseBlock = JassIm.ImStmts(); @@ -183,7 +184,7 @@ private ImFunction generateSetFunc(ImVar aVar, List newArrays) { ImFunction setFunc = JassIm.ImFunction(aVar.getTrace(), aVar.getName() + "_set", JassIm.ImTypeVars(), JassIm.ImVars(instanceId, arrayIndex, value), JassIm.ImVoid(), locals, body, Lists.newArrayList()); if (generateStacktraces) { - ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", false); + ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", Collections.emptyList()); setFunc.getParameters().add(stackPos); if (error.getFunc().getParameters().size() == 2) { error.getArguments().add(JassIm.ImVarAccess(stackPos)); @@ -220,10 +221,10 @@ private void generateBinSearchSet(ImStmts stmts, ImVar indexVar1, ImVar indexVar private ImFunction generateGetFunc(ImVar aVar, List newArrays) { ImArrayTypeMulti mtype = (ImArrayTypeMulti) aVar.getType(); - ImVar returnVal = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "returnVal", false); + ImVar returnVal = JassIm.ImVar(aVar.getTrace(), mtype.getEntryType(), "returnVal", Collections.emptyList()); ImVars locals = JassIm.ImVars(returnVal); - ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index1", false); - ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index2", false); + ImVar instanceId = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index1", Collections.emptyList()); + ImVar arrayIndex = JassIm.ImVar(aVar.getTrace(), TypesHelper.imInt(), "index2", Collections.emptyList()); ImFunctionCall error = imError(aVar, "Index out of Bounds"); ImStmts thenBlock = JassIm.ImStmts(error); ImStmts elseBlock = JassIm.ImStmts(); @@ -237,7 +238,7 @@ private ImFunction generateGetFunc(ImVar aVar, List newArrays) { ImFunction getFunc = JassIm.ImFunction(aVar.getTrace(), aVar.getName() + "_get", JassIm.ImTypeVars(), JassIm.ImVars(instanceId, arrayIndex), mtype.getEntryType(), locals, body, Lists.newArrayList()); if (generateStacktraces) { - ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", false); + ImVar stackPos = JassIm.ImVar(aVar.getTrace(), TypesHelper.imString(), "stackPos", Collections.emptyList()); getFunc.getParameters().add(stackPos); if (error.getFunc().getParameters().size() == 2) { error.getArguments().add(JassIm.ImVarAccess(stackPos)); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java index 8115aee9f..6cc76edbe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/OverrideUtils.java @@ -116,8 +116,7 @@ public static void addOverride( ImFunction implementation = JassIm.ImFunction(e, subMethod.getName() + "_wrapper", JassIm.ImTypeVars(), parameters, rType, locals, body, flags); tr.getImProg().getFunctions().add(implementation); - List subMethods = Collections.emptyList(); - ImMethod wrapperMethod = JassIm.ImMethod(e, subMethod.getMethodClass(), subMethod.getName() + "_wrapper", implementation, subMethods, false); + ImMethod wrapperMethod = JassIm.ImMethod(e, subMethod.getMethodClass(), subMethod.getName() + "_wrapper", implementation, new ArrayList<>(), false); subClass.getMethods().add(wrapperMethod); superMethodIm.getSubMethods().add(wrapperMethod); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java index fb1cc016b..207de8c7f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/RecycleCodeGeneratorQueue.java @@ -3,9 +3,10 @@ import de.peeeq.wurstscript.WurstOperator; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.*; -import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Constants; +import java.util.Collections; + /** * Manages object ids in a queue. This way the time each object is * inactive is maximized and thus errors should be easier to detect @@ -20,7 +21,7 @@ public void createAllocFunc(ImTranslator translator, ImProg prog, ImClass c) { ImStmts body = f.getBody(); Element tr = c.getTrace(); - ImVar thisVar = JassIm.ImVar(tr, translator.selfType(c), "this", false); // TODO change type + ImVar thisVar = JassIm.ImVar(tr, translator.selfType(c), "this", Collections.emptyList()); // TODO change type locals.add(thisVar); ClassManagementVars mVars = translator.getClassManagementVarsFor(c); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java index ad744bd01..4252f2d17 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StackTraceInjector2.java @@ -8,14 +8,12 @@ import de.peeeq.datastructures.TransitiveClosure; import de.peeeq.wurstio.TimeTaker; import de.peeeq.wurstscript.WurstOperator; -import de.peeeq.wurstscript.ast.FuncDef; import de.peeeq.wurstscript.ast.FunctionDefinition; import de.peeeq.wurstscript.ast.NameDef; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.TypesHelper; -import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; import java.util.*; @@ -79,9 +77,9 @@ public void visit(ImFuncRef imFuncRef) { }); de.peeeq.wurstscript.ast.Element trace = prog.attrTrace(); - stackSize = JassIm.ImVar(trace, TypesHelper.imInt(), "wurst_stack_depth", false); + stackSize = JassIm.ImVar(trace, TypesHelper.imInt(), "wurst_stack_depth", Collections.emptyList()); prog.getGlobals().add(stackSize); - stack = JassIm.ImVar(trace, TypesHelper.imStringArray(), "wurst_stack", false); + stack = JassIm.ImVar(trace, TypesHelper.imStringArray(), "wurst_stack", Collections.emptyList()); prog.getGlobals().add(stack); prog.getGlobalInits().put(stackSize, Collections.singletonList(JassIm.ImIntVal(0))); @@ -193,7 +191,7 @@ public void visit(ImReturn imReturn) { if (count > 1) { tempReturnName += "_" + count; } - ImVar temp = JassIm.ImVar(trace, f.getReturnType(), tempReturnName, false); + ImVar temp = JassIm.ImVar(trace, f.getReturnType(), tempReturnName, Collections.emptyList()); f.getLocals().add(temp); stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(temp), (ImExpr) returnedValue)); newReturn = JassIm.ImReturn(trace, JassIm.ImVarAccess(temp)); @@ -265,7 +263,7 @@ private void passStacktraceParams(final Multimap cal Collection callsForF = calls.get(f); // add stackPos parameter - f.getParameters().add(getStacktraceIndex(f), JassIm.ImVar(f.getTrace(), TypesHelper.imString(), STACK_POS_PARAM, false)); + f.getParameters().add(getStacktraceIndex(f), JassIm.ImVar(f.getTrace(), TypesHelper.imString(), STACK_POS_PARAM, Collections.emptyList())); // pass the stacktrace parameter at all calls for (ImFunctionCall call : callsForF) { String callPos = getCallPos(call.attrTrace().attrErrorPos()); @@ -390,11 +388,11 @@ private void rewriteErrorStatements(final Multimap } de.peeeq.wurstscript.ast.Element trace = s.attrTrace(); - ImVar traceStr = JassIm.ImVar(trace, TypesHelper.imString(), "stacktraceStr", false); + ImVar traceStr = JassIm.ImVar(trace, TypesHelper.imString(), "stacktraceStr", Collections.emptyList()); f.getLocals().add(traceStr); - ImVar traceI = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceIndex", false); + ImVar traceI = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceIndex", Collections.emptyList()); f.getLocals().add(traceI); - ImVar traceLimit = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceLimit", false); + ImVar traceLimit = JassIm.ImVar(trace, TypesHelper.imInt(), "stacktraceLimit", Collections.emptyList()); f.getLocals().add(traceLimit); ImStmts stmts = JassIm.ImStmts(); stmts.add(JassIm.ImSet(trace, JassIm.ImVarAccess(traceStr), JassIm.ImStringVal(""))); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java index 48e57c14e..56709c93e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/StmtTranslation.java @@ -9,16 +9,15 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.jassIm.ImExprs; import de.peeeq.wurstscript.jassIm.ImFunction; -import de.peeeq.wurstscript.jassIm.ImFunctionCall; import de.peeeq.wurstscript.jassIm.ImIf; import de.peeeq.wurstscript.jassIm.ImReturn; import de.peeeq.wurstscript.jassIm.ImSet; import de.peeeq.wurstscript.jassIm.ImStmts; import de.peeeq.wurstscript.jassIm.ImVar; -import de.peeeq.wurstscript.types.TypesHelper; -import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeVararg; +import de.peeeq.wurstscript.types.*; +import io.vavr.control.Option; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -72,10 +71,6 @@ public static ImStmt translate(StmtForFrom s, ImTranslator t, ImFunction f) { FuncLink nextFunc = nextFuncOpt.get(); FuncLink hasNextFunc = hasNextFuncOpt.get(); - // get the iterator function in the intermediate language - ImFunction nextFuncIm = t.getFuncFor(nextFunc.getDef()); - ImFunction hasNextFuncIm = t.getFuncFor(hasNextFunc.getDef()); - f.getLocals().add(t.getVarFor(s.getLoopVar())); ImExprs fromTarget; @@ -84,7 +79,7 @@ public static ImStmt translate(StmtForFrom s, ImTranslator t, ImFunction f) { } else { // store from-expression in variable, so that it is only evaluated once ImExpr iterationTargetTr = iterationTarget.imTranslateExpr(t, f); - ImVar fromVar = ImVar(s, iterationTargetTr.attrTyp(), "from", false); + ImVar fromVar = ImVar(s, iterationTargetTr.attrTyp(), "from", Collections.emptyList()); f.getLocals().add(fromVar); result.add(ImSet(s, ImVarAccess(fromVar), iterationTargetTr)); fromTarget = JassIm.ImExprs(ImVarAccess(fromVar)); @@ -92,10 +87,9 @@ public static ImStmt translate(StmtForFrom s, ImTranslator t, ImFunction f) { ImStmts imBody = ImStmts(); // exitwhen not #hasNext() - imBody.add(ImExitwhen(s, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(ImFunctionCall(s, hasNextFuncIm, ImTypeArguments(), fromTarget, false, CallType - .NORMAL))))); + imBody.add(ImExitwhen(s, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(functionCall(s, hasNextFunc, fromTarget, t))))); // elem = next() - ImFunctionCall nextCall = ImFunctionCall(s, nextFuncIm, ImTypeArguments(), fromTarget.copy(), false, CallType.NORMAL); + ImExpr nextCall = functionCall(s, nextFunc, fromTarget.copy(), t); WurstType nextReturn = nextFunc.getReturnType(); ImExpr nextCallWrapped = ExprTranslation.wrapTranslation(s, t, nextCall, nextReturn, loopVarType); @@ -130,11 +124,6 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { // Type of loop Variable: WurstType loopVarType = forIn.getLoopVar().attrTyp(); - // get the iterator function in the intermediate language - ImFunction iteratorFuncIm = t.getFuncFor(iteratorFunc.getDef()); - ImFunction nextFuncIm = t.getFuncFor(nextFunc.getDef()); - ImFunction hasNextFuncIm = t.getFuncFor(hasNextFunc.getDef()); - // translate target: ImExprs iterationTargetList; if (forIn.getIn().attrTyp().isStaticRef()) { @@ -145,9 +134,10 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { } // call XX.iterator() - ImFunctionCall iteratorCall = ImFunctionCall(forIn, iteratorFuncIm, ImTypeArguments(), iterationTargetList, false, CallType.NORMAL); + // TODO use method calls if method, set type args (reuse code if possible) + ImExpr iteratorCall = functionCall(forIn, iteratorFunc, iterationTargetList, t); // create IM-variable for iterator - ImVar iteratorVar = JassIm.ImVar(forIn.getLoopVar(), iteratorCall.attrTyp(), "iterator", false); + ImVar iteratorVar = ImVar(forIn.getLoopVar(), iteratorCall.attrTyp(), "iterator", Collections.emptyList()); f.getLocals().add(iteratorVar); f.getLocals().add(t.getVarFor(forIn.getLoopVar())); @@ -159,11 +149,11 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { ImStmts imBody = ImStmts(); // exitwhen not #hasNext() - imBody.add(ImExitwhen(forIn, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(ImFunctionCall(forIn, hasNextFuncIm, ImTypeArguments(), JassIm.ImExprs - (JassIm - .ImVarAccess(iteratorVar)), false, CallType.NORMAL))))); + imBody.add(ImExitwhen(forIn, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(functionCall(forIn, hasNextFunc, JassIm.ImExprs + (JassIm + .ImVarAccess(iteratorVar)), t))))); // elem = next() - ImFunctionCall nextCall = ImFunctionCall(forIn, nextFuncIm, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(iteratorVar)), false, CallType.NORMAL); + ImExpr nextCall = functionCall(forIn, nextFunc, JassIm.ImExprs(JassIm.ImVarAccess(iteratorVar)), t); WurstType nextReturn = nextFunc.getReturnType(); ImExpr nextCallWrapped = ExprTranslation.wrapTranslation(forIn, t, nextCall, nextReturn, loopVarType); @@ -179,8 +169,8 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { @Override public void visit(ImReturn imReturn) { super.visit(imReturn); - imReturn.replaceBy(ImHelper.statementExprVoid(JassIm.ImStmts(ImFunctionCall(forIn, t.getFuncFor(funcLink.getDef()), ImTypeArguments(), JassIm - .ImExprs(JassIm.ImVarAccess(iteratorVar)), false, CallType.NORMAL), imReturn.copy()))); + imReturn.replaceBy(ImHelper.statementExprVoid(JassIm.ImStmts(functionCall(forIn, funcLink, JassIm + .ImExprs(JassIm.ImVarAccess(iteratorVar)), t), imReturn.copy()))); } }); @@ -189,8 +179,8 @@ public void visit(ImReturn imReturn) { result.add(ImLoop(forIn, imBody)); // close iterator after loop - closeFunc.ifPresent(nameLink -> result.add(ImFunctionCall(forIn, t.getFuncFor(nameLink.getDef()), ImTypeArguments(), JassIm.ImExprs(JassIm - .ImVarAccess(iteratorVar)), false, CallType.NORMAL))); + closeFunc.ifPresent(nameLink -> result.add(functionCall(forIn, nameLink, JassIm.ImExprs(JassIm + .ImVarAccess(iteratorVar)), t))); } @@ -198,6 +188,38 @@ public void visit(ImReturn imReturn) { return ImHelper.statementExprVoid(ImStmts(result)); } + private static ImExpr functionCall(Element trace, FuncLink funcLink, ImExprs args, ImTranslator tr) { + FunctionDefinition def = funcLink.getDef(); + ImTypeArguments typeParams = JassIm.ImTypeArguments(); + if (def instanceof AstElementWithTypeParameters) { + VariableBinding binding = funcLink.getVariableBinding(); + for (TypeParamDef tp : ((AstElementWithTypeParameters) def).getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + WurstTypeBoundTypeParam t = binding.get(tp).get(); + typeParams.add(JassIm.ImTypeArgument(t.imTranslateType(tr))); + } + } + } + if (def.attrIsDynamicClassMember()) { + FuncDef funcDef = (FuncDef) def; + + // translate as method call + args = args.copyWithRefs(); + ImExpr receiver = args.remove(0); + return JassIm.ImMethodCall(trace, tr.getMethodFor(funcDef), + typeParams, receiver, args, false); + } + // otherwise it is a function call: + return JassIm.ImFunctionCall( + trace, + tr.getFuncFor(def), + typeParams, + args, + false, + CallType.NORMAL + ); + } + /** * Translate a for in vararg loop. Unlike the other for loops we don't need * an iterator etc. because the loop is unrolled in the VarargEliminator @@ -215,13 +237,13 @@ private static ImStmt case_StmtForVararg(StmtForIn s, ImTranslator t, ImFunction public static ImStmt translate(StmtForRangeUp s, ImTranslator t, ImFunction f) { return case_StmtForRange(t, f, s.getLoopVar(), s.getTo(), s.getStep(), s.getBody(), WurstOperator.PLUS, - WurstOperator.GREATER, s); + WurstOperator.GREATER, s); } public static ImStmt translate(StmtForRangeDown s, ImTranslator t, ImFunction f) { return case_StmtForRange(t, f, s.getLoopVar(), s.getTo(), s.getStep(), s.getBody(), - WurstOperator.MINUS, WurstOperator.LESS, s); + WurstOperator.MINUS, WurstOperator.LESS, s); } private static ImStmt case_StmtForRange(ImTranslator t, ImFunction f, LocalVarDef loopVar, @@ -254,7 +276,7 @@ private static ImExpr addCacheVariableSmart(ImTranslator t, ImFunction f, Listmap(e -> ImOperatorCall(WurstOperator.EQ, ImExprs(tempVar.copy(), e.imTranslateExpr(t, f)))) - .reduce((x, y) -> ImOperatorCall(WurstOperator.OR, ImExprs(x, y))) - .orElseGet(() -> JassIm.ImBoolVal(true)); + .stream() + .map(e -> ImOperatorCall(WurstOperator.EQ, ImExprs(tempVar.copy(), e.imTranslateExpr(t, f)))) + .reduce((x, y) -> ImOperatorCall(WurstOperator.OR, ImExprs(x, y))) + .orElseGet(() -> JassIm.ImBoolVal(true)); } public static ImStmt translate(EndFunctionStatement endFunctionStatement, ImTranslator translator, ImFunction f) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java index c960824dc..ff482d4e1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TLDTranslation.java @@ -5,7 +5,6 @@ import de.peeeq.wurstscript.jassIm.ImStmt; import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.WurstTypeClass; -import org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -112,7 +111,6 @@ public static void translate(InitBlock initBlock, ImTranslator translator) { public static void translate(InterfaceDef interfaceDef, ImTranslator translator) { new InterfaceTranslator(interfaceDef, translator).translate(); - } @@ -134,4 +132,7 @@ public static void translate(ModuleInstanciation moduleInstanciation, ImTranslat // nothing to do? } + public static void translate(InstanceDecl instanceDecl, ImTranslator translator) { + TypeClassTranslator.translateTypeClass(instanceDecl, translator); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java new file mode 100644 index 000000000..0cec51e4f --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeClassTranslator.java @@ -0,0 +1,55 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.names.FuncLink; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.validation.WurstValidator; + +import java.util.ArrayList; +import java.util.List; + +public class TypeClassTranslator { + + + public static void translateTypeClass(InstanceDecl instance, ImTranslator tr) { + ImClass c = tr.getClassFor(instance); + tr.getImProg().getClasses().add(c); + + WurstType implementedInterface = instance.getImplementedInterface().attrTyp(); + c.getSuperClasses().add((ImClassType) implementedInterface.imTranslateType(tr)); + + for (TypeParamDef tp : instance.getTypeParameters()) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { + for (TypeParamConstraint constraint : ((TypeParamConstraintList) tp.getTypeParamConstraints())) { + ImVar v = tr.getTypeClassParamFor(constraint); + c.getFields().add(v); + } + } + } + + for (FuncDef method : instance.getMethods()) { + translateMethod(instance, c, method, implementedInterface, tr); + } + + } + + private static void translateMethod(InstanceDecl instance, ImClass c, FuncDef funcDef, WurstType implementedInterface, ImTranslator tr) { + ImFunction func = tr.getFuncFor(funcDef); + func.getBody().addAll(tr.translateStatements(func, funcDef.getBody())); + + ImMethod m = tr.getMethodFor(funcDef); + c.getMethods().add(m); + + m.setImplementation(func); + + List superMethods = new ArrayList<>(); + implementedInterface.addMemberMethods(funcDef, funcDef.getName(), superMethods); + for (FuncLink superMethod : superMethods) { + if (WurstValidator.canOverride(funcDef.createFuncLink(instance), superMethod, false)) { + OverrideUtils.addOverride(tr, (FuncDef) superMethod.getDef(), c, m, funcDef, implementedInterface.getTypeArgBinding()); + } + } + + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarFlag.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarFlag.java new file mode 100644 index 000000000..8700aacdd --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarFlag.java @@ -0,0 +1,15 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import de.peeeq.wurstscript.jassIm.ImVar; + +public enum VarFlag { + /** this is a variable from blizzard.j */ + BJ, + /** this is a parameter that should be specialized: + * for each subclass a copy of the function should be created by the optimizer */ + SPECIALIZE; + + public static boolean isBj(ImVar v) { + return v.getVarFlags().contains(BJ); + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarargEliminator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarargEliminator.java index 8458d5420..8e4d94afe 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarargEliminator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarargEliminator.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -84,7 +85,7 @@ private void generateVarargFunc(ImFunction func, int numberOfParams) { ImType type = varargParam.getType(); List newParams = new ArrayList<>(); for (int i = 0; i < argumentSize; i++) { - ImVar param = JassIm.ImVar(func.getTrace(), type, varargParam.getName() + "_" + i, false); + ImVar param = JassIm.ImVar(func.getTrace(), type, varargParam.getName() + "_" + i, Collections.emptyList()); newParams.add(param); newFunc.getParameters().add(param); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java index 0775dc668..928aaa58e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java @@ -86,7 +86,8 @@ public static LuaExpr translate(ImIntVal e, LuaTranslator tr) { } public static LuaExpr translate(ImMemberAccess e, LuaTranslator tr) { - LuaExpr res = LuaAst.LuaExprFieldAccess(e.getReceiver().translateToLua(tr), e.getVar().getName()); + LuaVariable luaV = tr.luaVar.getFor(e.getVar()); + LuaExpr res = LuaAst.LuaExprFieldAccess(e.getReceiver().translateToLua(tr), luaV.getName()); if (!e.getIndexes().isEmpty()) { LuaExprlist indexes = LuaAst.LuaExprlist(); for (ImExpr index : e.getIndexes()) { @@ -158,6 +159,37 @@ && isStringType(right.attrTyp())) { throw new Error("not implemented: " + e); } + public static LuaExpr translate(ImTypeClassDictValue e, LuaTranslator tr) { + ImClass c = e.getClazz().getClassDef(); + LuaMethod m = tr.luaClassInitMethod.getFor(c); + LuaVariable classVar = tr.luaClassVar.getFor(c); + + LuaStatements body = LuaAst.LuaStatements(); + LuaVariable r = LuaAst.LuaVariable("res", LuaAst.LuaExprMethodCall( + LuaAst.LuaExprVarAccess(classVar), + m, + LuaAst.LuaExprlist() + )); + body.add(r); + int i = 0; + for (ImVar field : c.getFields()) { + System.out.println("field: " + field); + if (i >= e.getArguments().size()) { + break; + } + ImExpr arg = e.getArguments().get(i); + + LuaVariable lField = tr.luaVar.getFor(field); + body.add(LuaAst.LuaAssignment( + LuaAst.LuaExprFieldAccess(LuaAst.LuaExprVarAccess(r), lField.getName()), + arg.translateToLua(tr))); + } + body.add(LuaAst.LuaReturn(LuaAst.LuaExprVarAccess(r))); + return LuaAst.LuaExprFunctionCallE( + LuaAst.LuaExprFunctionAbstraction(LuaAst.LuaParams(), body), + LuaAst.LuaExprlist()); + } + static class TupleFunc { final ImTupleType tupleType; final LuaFunction func; @@ -330,9 +362,6 @@ public static LuaExpr translate(ImCompiletimeExpr imCompiletimeExpr, LuaTranslat throw new Error("not implemented"); } - public static LuaExpr translate(ImTypeVarDispatch imTypeVarDispatch, LuaTranslator tr) { - throw new Error("not implemented"); - } public static LuaExpr translate(ImCast imCast, LuaTranslator tr) { LuaExpr translated = imCast.getExpr().translateToLua(tr); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index 2b3906ce4..5219935d1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -1,14 +1,12 @@ package de.peeeq.wurstscript.translation.lua.translation; import de.peeeq.datastructures.UnionFind; -import de.peeeq.wurstio.TimeTaker; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.FuncDef; import de.peeeq.wurstscript.ast.NameDef; import de.peeeq.wurstscript.ast.WPackage; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.luaAst.*; -import de.peeeq.wurstscript.translation.imoptimizer.ImOptimizer; import de.peeeq.wurstscript.translation.imtranslation.*; import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Lazy; @@ -16,7 +14,6 @@ import org.jetbrains.annotations.NotNull; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import static de.peeeq.wurstscript.translation.lua.translation.ExprTranslation.WURST_SUPERTYPES; @@ -27,7 +24,7 @@ public class LuaTranslator { final LuaCompilationUnit luaModel; private final Set usedNames = new HashSet<>(Arrays.asList( // reserved function names - "print", "tostring", "error", + "print", "tostring", "error", "main", // keywords: "and", "break", @@ -63,19 +60,31 @@ private ImProg getProg() { @Override public LuaVariable initFor(ImVar a) { String name = a.getName(); - if (!a.getIsBJ()) { + if (!a.isBJ()) { name = uniqueName(name); } return LuaAst.LuaVariable(name, LuaAst.LuaNoExpr()); } }; + GetAForB luaTypeClassDictionaryVar = new GetAForB() { + @Override + public LuaVariable initFor(ImTypeVar a) { + String name = uniqueName(a.getName()); + return LuaAst.LuaVariable(name, LuaAst.LuaNoExpr()); + } + }; + + + GetAForB luaFunc = new GetAForB() { @Override public LuaFunction initFor(ImFunction a) { String name = a.getName(); - if (!a.isExtern() && !a.isBj() && !a.isNative()) { + if (!a.isExtern() && !a.isBj() && !a.isNative() + && imTr.getMainFunc() != a + && imTr.getConfFunc() != a) { name = uniqueName(name); } @@ -218,7 +227,7 @@ private void collectPredefinedNames() { } for (ImVar global : prog.getGlobals()) { - if (global.getIsBJ()) { + if (global.isBJ()) { setNameFromTrace(global); usedNames.add(global.getName()); } @@ -494,8 +503,9 @@ private void createClassInitFunction(ImClass c, LuaVariable classVar, LuaMethod LuaTableFields initialFieldValues = LuaAst.LuaTableFields(); LuaVariable newInst = LuaAst.LuaVariable("new_inst", LuaAst.LuaTableConstructor(initialFieldValues)); for (ImVar field : c.getFields()) { + LuaVariable fieldL = luaVar.getFor(field); initialFieldValues.add( - LuaAst.LuaTableNamedField(field.getName(), defaultValue(field.getType())) + LuaAst.LuaTableNamedField(fieldL.getName(), defaultValue(field.getType())) ); } @@ -575,7 +585,7 @@ private void collectSuperClasses(LuaTableFields superClasses, ImClass c, Set errors; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java new file mode 100644 index 000000000..8d143684f --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypeClassInstance.java @@ -0,0 +1,106 @@ +package de.peeeq.wurstscript.types; + + +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.ast.Element; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import de.peeeq.wurstscript.utils.Utils; +import org.eclipse.xtend.lib.annotations.ToString; + +import java.util.List; + +public abstract class TypeClassInstance { + + public static TypeClassInstance fromInstance(InstanceDecl decl, List typeArgs, List dependencies, WurstTypeInterface constraint) { + return new TypeClassInstance() { + @Override + public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { + ImClass c = tr.getClassFor(decl); + ImTypeArguments imTypeArgs = JassIm.ImTypeArguments(); + for (WurstType ta : typeArgs) { + imTypeArgs.add(JassIm.ImTypeArgument(ta.imTranslateType(tr))); + } + ImClassType ct = JassIm.ImClassType(c, imTypeArgs); + ImExprs args = JassIm.ImExprs(); + for (TypeClassInstance dep : dependencies) { + ImExpr d = dep.translate(trace, thisVar, tr); + args.add(d); + } + return JassIm.ImTypeClassDictValue(trace, ct, args); + } + + @Override + public String toString() { + return "Instance " + decl.getImplementedInterface().attrTyp() + " " + Utils.printElementSource(decl); + } + }; + } + + public static TypeClassInstance fromTypeParam(Element trace, TypeParamConstraint constraint) { + return new TypeClassInstance() { + @Override + public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { + ImVar param = tr.getTypeClassParamFor(constraint); + if (isLocalVar(param)) { + return JassIm.ImVarAccess(param); + } else { + if (thisVar == null) { + throw new CompileError(trace, "Cannot construct " + this + ", because there is no 'this' available"); + } + // if it is a class field, access the field: + return JassIm.ImMemberAccess(trace, + JassIm.ImVarAccess(thisVar), + JassIm.ImTypeArguments(), + param, JassIm.ImExprs()); + } + } + + @Override + public String toString() { + return "Instance from constraint of type parameter " + constraint.parentTypeParam().getName() + " " + Utils.printElementSource(constraint); + } + }; + } + + private static boolean isLocalVar(ImVar param) { + return param != null + && param.getParent() != null + && param.getParent().getParent() instanceof ImFunction; + } + + public static TypeClassInstance fromObject(WurstTypeClassOrInterface objectType, InterfaceDef supI) { + return new TypeClassInstance() { + @Override + public ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr) { + ImClass sup = tr.getClassFor(supI); + ImClass cd = tr.getGenericObjectTypeClassInstance(); + // add instance as superclass if not present yet + if (cd.getSuperClasses().stream().noneMatch(sc -> sc.getClassDef() == sup)) { + cd.getSuperClasses().add(JassIm.ImClassType(sup, + JassIm.ImTypeArguments(JassIm.ImTypeArgument(objectType.imTranslateType(tr))))); + // add sub methods + supI.attrTyp().getMemberMethods(trace).forEach(m -> { + ImMethod imMethod = tr.getMethodFor((FuncDef) m.getDef()); + for (ImMethod cdM : cd.getMethods()) { + if (cdM.getName().equals(m.getName())) { + imMethod.getSubMethods().add(cdM); + } + } + }); + } + ImClassType c = JassIm.ImClassType(cd, JassIm.ImTypeArguments()); + return JassIm.ImTypeClassDictValue(trace, c, JassIm.ImExprs()); + } + + @Override + public String toString() { + return "Object type class instance"; + } + }; + } + + + public abstract ImExpr translate(Element trace, ImVar thisVar, ImTranslator tr); +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java index 33ec5e88f..6a1823d22 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/VariableBinding.java @@ -100,7 +100,7 @@ public String toString() { } s.append(e._1().getName()); s.append(" -> "); - s.append(e._2().getBaseType()); + s.append(e._2().getName()); first = false; } s.append("]"); @@ -144,4 +144,11 @@ public VariableBinding withError(CompileError err) { public List getErrors() { return errors; } + + public VariableBinding withTypeClassInstance(TypeParamDef tp, WurstTypeBoundTypeParam matchedType, TypeClassInstance instance) { + WurstTypeBoundTypeParam bound = get(tp) + .getOrElse(matchedType); + bound = bound.withTypeClassInstance(instance); + return set(tp, bound); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java index 48abeaf90..e812a0b4c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeBoundTypeParam.java @@ -1,17 +1,17 @@ package de.peeeq.wurstscript.types; +import com.google.common.collect.ImmutableList; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.ImplicitFuncs; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; -import io.vavr.control.Either; +import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import static de.peeeq.wurstscript.types.VariablePosition.NONE; @@ -24,7 +24,8 @@ public class WurstTypeBoundTypeParam extends WurstType { // the fromIndex and toIndex functions for old-generics: private FuncDef fromIndex; private FuncDef toIndex; - private final @Nullable Map typeConstraintFunctions; + // type class instances (null for old generics) + private final @Nullable List instances; private boolean indexInitialized = false; private Element context; @@ -36,12 +37,22 @@ public WurstTypeBoundTypeParam(TypeParamDef def, WurstType baseType, Element con this.baseType = baseType; this.context = context; if (def.getTypeParamConstraints() instanceof NoTypeParamConstraints) { - this.typeConstraintFunctions = null; + this.instances = null; } else { - this.typeConstraintFunctions = new HashMap<>(); + this.instances = Collections.emptyList(); } } + public WurstTypeBoundTypeParam(TypeParamDef typeParamDef, WurstType baseType, FuncDef fromIndex, FuncDef toIndex, @Nullable List instances, boolean indexInitialized, Element context) { + this.typeParamDef = typeParamDef; + this.baseType = baseType; + this.fromIndex = fromIndex; + this.toIndex = toIndex; + this.instances = instances; + this.indexInitialized = indexInitialized; + this.context = context; + } + @Override VariableBinding matchAgainstSupertypeIntern(WurstType other, @Nullable Element location, VariableBinding mapping, VariablePosition variablePosition) { return baseType.matchAgainstSupertypeIntern(other, location, mapping, NONE); @@ -50,7 +61,8 @@ VariableBinding matchAgainstSupertypeIntern(WurstType other, @Nullable Element l @Override public String getName() { return baseType.getName(); -// return "[" + typeParamDef.getName() + ": " + baseType + "]"; +// String is = instances == null ? "" : " | " + Utils.printSep(", ", instances); +// return "[" + typeParamDef.getName() + ": " + baseType + is + "]"; } @Override @@ -130,7 +142,7 @@ private void initIndex() { if (indexInitialized) { return; } - if (typeConstraintFunctions == null) { + if (instances == null) { if (!baseType.supportsGenerics()) { // if type does support generics natively, try to find implicit conversion functions fromIndex = ImplicitFuncs.findFromIndexFunc(baseType, context); @@ -157,6 +169,10 @@ protected boolean isNullable() { @Override public WurstTypeBoundTypeParam setTypeArgs(VariableBinding typeParamMapping) { +// Option m = typeParamMapping.get(typeParamDef); +// if (m.isDefined()) { +// return m.get(); +// } return this.withBaseType(baseType.setTypeArgs(typeParamMapping)); } @@ -190,18 +206,25 @@ public boolean isTranslatedToInt() { return baseType.isTranslatedToInt(); } - public @Nullable Map getTypeConstraintFunctions() { - return typeConstraintFunctions; - } - public boolean isTemplateTypeParameter() { - return typeParamDef.getTypeParamConstraints() instanceof TypeExprList; + return typeParamDef.getTypeParamConstraints() instanceof TypeParamConstraintList; } public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { ImType t = imTranslateType(tr); - Map> typeClassBinding = new HashMap<>(); - // TODO add type class binding - return JassIm.ImTypeArgument(t, typeClassBinding); + return JassIm.ImTypeArgument(t); + } + + public WurstTypeBoundTypeParam withTypeClassInstance(TypeClassInstance instance) { + ImmutableList newInstances = + ImmutableList.builderWithExpectedSize(instances.size() + 1) + .addAll(instances) + .add(instance) + .build(); + return new WurstTypeBoundTypeParam(typeParamDef, baseType, fromIndex, toIndex, newInstances, indexInitialized, context); + } + + public @Nullable List getInstances() { + return instances; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java index 2c5c5e9de..ef415a703 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClassOrInterface.java @@ -10,12 +10,14 @@ import de.peeeq.wurstscript.attributes.names.DefLink; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.jassIm.ImClassType; import de.peeeq.wurstscript.jassIm.ImType; import de.peeeq.wurstscript.jassIm.ImTypeArguments; import de.peeeq.wurstscript.jassIm.JassIm; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -131,14 +133,20 @@ VariableBinding matchAgainstSupertypeIntern(WurstType obj, @Nullable Element loc } @Override - public final ImType imTranslateType(ImTranslator tr) { + public final ImClassType imTranslateType(ImTranslator tr) { + ImTypeArguments typeArgs = translateTypeArguments(tr); + return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs); + } + + @NotNull + private ImTypeArguments translateTypeArguments(ImTranslator tr) { ImTypeArguments typeArgs = JassIm.ImTypeArguments(); for (WurstTypeBoundTypeParam btp : getTypeParameters()) { if (btp.isTemplateTypeParameter()) { typeArgs.add(btp.imTranslateToTypeArgument(tr)); } } - return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs); + return typeArgs; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java index e83baf8ee..3980a4008 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInterface.java @@ -60,14 +60,16 @@ public WurstType dynamic() { } @Override - public WurstType replaceTypeVars(List newTypes) { + public WurstTypeInterface replaceTypeVars(List newTypes) { return new WurstTypeInterface(getInterfaceDef(), newTypes); } public ImmutableList extendedInterfaces() { return interfaceDef.getExtendsList().stream() - .map(i -> (WurstTypeInterface) i.attrTyp().setTypeArgs(getTypeArgBinding())) + .map(i -> i.attrTyp().setTypeArgs(getTypeArgBinding())) + .filter(i -> i instanceof WurstTypeInterface) + .map(i -> (WurstTypeInterface) i) .filter(i -> i.level() < level()) .collect(ImmutableList.toImmutableList()); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java index ffebb8882..8a64cee4f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNamedScope.java @@ -1,14 +1,21 @@ package de.peeeq.wurstscript.types; import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Lists; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.*; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; +import io.vavr.Tuple2; +import io.vavr.control.Option; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; public abstract class WurstTypeNamedScope extends WurstType { @@ -108,30 +115,76 @@ public WurstType setTypeArgs(@NonNull VariableBinding typeParamBounds) { abstract public WurstType replaceTypeVars(List newTypes); - public WurstType replaceTypeVarsUsingTypeArgs(TypeExprList typeArgs) { + public WurstType replaceTypeVarsUsingTypeArgs(TypeExprList typeArgs1) { + + List typeArgs = typeArgs1; + + if (typeArgs1.getParent().getParent() instanceof TypeParamConstraint) { + // if used as type parameter constraint, then add type param as last type argument + TypeParamConstraint constraint = (TypeParamConstraint) typeArgs1.getParent().getParent(); + WurstTypeTypeParam tp = new WurstTypeTypeParam(constraint.parentTypeParam(), true); + typeArgs = ImmutableList.builder() + .addAll(typeArgs) + .add(Ast.TypeExprResolved(typeArgs1.attrSource(), tp)) + .build(); + } + if (typeArgs.isEmpty()) { // TODO replace with unknown types? return this; } - List typeParams = new ArrayList<>(); + + if (typeArgs.size() != typeParameters.size()) { - typeArgs.addError("Expected " + typeParameters.size() + " type arguments, but got " + typeArgs.size()); + typeArgs1.addError("Expected " + typeParameters.size() + " type arguments, but got " + typeArgs.size()); } + List errors = new ArrayList<>(); + VariableBinding mapping = VariableBinding.emptyMapping(); + for (int i = 0; i < typeArgs.size() && i < typeParameters.size(); i++) { WurstTypeBoundTypeParam tp = typeParameters.get(i); TypeParamDef tpDef = tp.getTypeParamDef(); TypeExpr typeArg = typeArgs.get(i); WurstType baseType = typeArg.attrTyp().dynamic(); - typeParams.add(new WurstTypeBoundTypeParam(tpDef, baseType, typeArg)); + WurstTypeBoundTypeParam wtbt = new WurstTypeBoundTypeParam(tpDef, baseType, typeArg); + mapping = mapping.set(tpDef, wtbt); + } + + for (int i = 0; i < typeArgs.size() && i < typeParameters.size(); i++) { + WurstTypeBoundTypeParam tp = typeParameters.get(i); + TypeParamDef tpDef = tp.getTypeParamDef(); + WurstTypeBoundTypeParam wtbt = mapping.get(tpDef).get(); + TypeExpr typeArg = typeArgs.get(i); + for (WurstTypeInterface constraint : TypeClasses.getConstraints(tpDef)) { + VariableBinding mapping2 = TypeClasses.findTypeClass( + typeArg, + errors, + mapping, + tpDef, + wtbt, + constraint + ); + if (mapping2 != null) { + mapping = mapping2; + } + } + } + + for (CompileError error : errors) { + typeArgs.get(0).getErrorHandler().sendError(error); } -// List newTypes = node.getTypeArgs().stream() -// .map((TypeExpr te) -> te.attrTyp().dynamic()) -// .collect(Collectors.toList()); - return replaceTypeVars(typeParams); +// return setTypeArgs(mapping); + VariableBinding finalMapping = mapping; + List newTypes = + typeParameters.stream() + .map(t -> finalMapping.get(t.getTypeParamDef()) + .getOrElse(() -> new WurstTypeBoundTypeParam(t.getTypeParamDef(), WurstTypeUnknown.instance(), typeArgs1))) + .collect(Collectors.toList()); + return replaceTypeVars(newTypes); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java index 0e5a77def..047509675 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTypeParam.java @@ -1,21 +1,28 @@ package de.peeeq.wurstscript.types; -import de.peeeq.wurstscript.ast.Element; -import de.peeeq.wurstscript.ast.TypeExprList; -import de.peeeq.wurstscript.ast.TypeParamDef; +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; import de.peeeq.wurstscript.jassIm.JassIm; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import de.peeeq.wurstscript.utils.Utils; import io.vavr.control.Option; import org.eclipse.jdt.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public class WurstTypeTypeParam extends WurstType { - private TypeParamDef def; + private final boolean isStaticRef; + private final TypeParamDef def; - public WurstTypeTypeParam(TypeParamDef t) { + public WurstTypeTypeParam(TypeParamDef t, boolean isStaticRef) { this.def = t; + this.isStaticRef = isStaticRef; } @Override @@ -75,9 +82,11 @@ public ImType imTranslateType(ImTranslator tr) { return JassIm.ImAnyType(); } - /** Using the new template generics with type constraints*/ + /** + * Using the new template generics with type constraints + */ private boolean hasTypeConstraints() { - return def.getTypeParamConstraints() instanceof TypeExprList; + return def.getTypeParamConstraints() instanceof TypeParamConstraintList; } @Override @@ -96,4 +105,51 @@ protected boolean isNullable() { return !hasTypeConstraints(); } + @Override + public void addMemberMethods(Element node, String name, List result) { + getMemberMethods(node) + .filter(fl -> fl.getName().equals(name)) + .collect(Collectors.toCollection(() -> result)); + } + + @Override + public Stream getMemberMethods(Element node) { + if (def.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList constraints = (TypeParamConstraintList) def.getTypeParamConstraints(); + return constraints.stream() + .flatMap((TypeParamConstraint constr) -> { + WurstType t = constr.attrConstraintTyp(); + + if (t instanceof WurstTypeInterface) { + WurstTypeInterface wti = (WurstTypeInterface) t; + + return wti.getMemberMethods(node) + .map(f -> f.withReceiverType(this).withTypeParamConstraint(constr)); + } else { + return Stream.empty(); + } + }); + } else { + return Stream.empty(); + } + } + + public List getTypeConstraints() { + if (def.getTypeParamConstraints() instanceof TypeParamConstraintList) { + TypeParamConstraintList constraints = (TypeParamConstraintList) def.getTypeParamConstraints(); + return constraints; + } else { + return Collections.emptyList(); + } + } + + @Override + public boolean isStaticRef() { + return isStaticRef; + } + + @Override + public boolean allowsDynamicDispatch() { + return false; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java index 5f302ebef..79fcf08c7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Utils.java @@ -10,6 +10,7 @@ import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.attributes.prettyPrint.DefaultSpacer; +import de.peeeq.wurstscript.attributes.prettyPrint.PrettyPrinter; import de.peeeq.wurstscript.jassIm.JassImElementWithName; import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.WurstType; @@ -28,6 +29,7 @@ import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; public class Utils { @@ -316,6 +318,9 @@ public static String printElement(@Nullable Element e) { name += "}"; } type = "type"; + } else if (e instanceof InstanceDecl) { + InstanceDecl i = (InstanceDecl) e; + return "Type class instance for " + PrettyPrinter.print(i.getImplementedInterface()); } return type + " " + name; } @@ -471,8 +476,13 @@ public static boolean isEmptyCU(@Nullable CompilationUnit cu) { public static String printElementWithSource(Element e) { WPos src = e.attrSource(); - return printElement(e) + " (" + src.getFile() + ":" - + src.getLine() + ")"; + return printElement(e) + " " + printElementSource(e); + } + + public static String printElementSource(Element e) { + WPos src = e.attrSource(); + return "(" + src.getFile() + ":" + + src.getLine() + ")"; } public static int[] copyArray(int[] ar) { @@ -1057,6 +1067,40 @@ public static List init(List list) { return list.stream().limit(list.size() - 1).collect(Collectors.toList()); } + /** checks if two lists are equal using a custom equality function */ + public static boolean listEquals(List xs, List ys, BiPredicate cmp) { + if (xs.size() != ys.size()) { + return false; + } + Iterator xi = xs.iterator(); + Iterator yi = ys.iterator(); + while (xi.hasNext()) { + T x = xi.next(); + T y = yi.next(); + if (!cmp.test(x, y)) { + return false; + } + } + return true; + } + + public static Iterable> zip(Collection as, Collection bs) { + return () -> new Iterator>() { + Iterator ia = as.iterator(); + Iterator ib = bs.iterator(); + + @Override + public boolean hasNext() { + return ia.hasNext() && ib.hasNext(); + } + + @Override + public Pair next() { + return Pair.create(ia.next(), ib.next()); + } + }; + } + public static class ExecResult { private final String stdOut; private final String stdErr; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java index 5361158d3..413dca31d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java @@ -2,6 +2,7 @@ import com.google.common.collect.*; import de.peeeq.wurstio.utils.FileUtils; +import de.peeeq.wurstscript.TypeClasses; import de.peeeq.wurstscript.WLogger; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.CofigOverridePackages; @@ -339,6 +340,10 @@ private void check(Element e) { checkForInvalidStmts((WStatements) e); if (e instanceof StmtExitwhen) visit((StmtExitwhen) e); + if (e instanceof TypeParamDef) + checkTypeParamDef(((TypeParamDef) e)); + if (e instanceof InstanceDecl) + TypeClasses.checkInstance(((InstanceDecl) e)); } catch (CyclicDependencyError cde) { cde.printStackTrace(); Element element = cde.getElement(); @@ -350,6 +355,16 @@ private void check(Element e) { } } + private void checkTypeParamDef(TypeParamDef e) { + TypeParamConstraints constraints = e.getTypeParamConstraints(); + if (constraints instanceof TypeParamConstraintList) { + TypeParamConstraintList typeExprs = (TypeParamConstraintList) constraints; + for (TypeParamConstraint te : typeExprs) { + te.attrConstraintTyp(); + } + } + } + private void checkAbstractMethods(ClassDef c) { ImmutableMultimap nameLinks = c.attrNameLinks(); if (!c.attrIsAbstract()) { @@ -1538,7 +1553,7 @@ public VariableBinding case_ExprMemberMethodDotDot(ExprMemberMethodDotDot e) { WurstType typ = boundTyp.getBaseType(); TypeParamDef tp = t._1(); - if (tp.getTypeParamConstraints() instanceof TypeExprList) { + if (tp.getTypeParamConstraints() instanceof TypeParamConstraintList) { // new style generics } else { // old style generics @@ -1771,7 +1786,11 @@ public void case_FuncDef(FuncDef f) { } } } else { - check(VisibilityPublic.class, Annotation.class); + if (isInInstanceDecl(f)) { + check(VisibilityPublic.class, Annotation.class, ModOverride.class); + } else { + check(VisibilityPublic.class, Annotation.class); + } } if (f.attrIsCompiletime()) { if (f.getParameters().size() > 0) { @@ -1828,6 +1847,11 @@ public void case_EnumMember(EnumMember enumMember) { check(); } + @Override + public void case_InstanceDecl(InstanceDecl instanceDecl) { + check(VisibilityPublic.class); + } + }); if (error.length() > 0) { if (m.attrSource().getFile().endsWith(".jurst")) { @@ -1840,6 +1864,17 @@ public void case_EnumMember(EnumMember enumMember) { } } + private boolean isInInstanceDecl(FuncDef f) { + Element e = f; + while (e != null) { + if (e instanceof InstanceDecl) { + return true; + } + e = e.getParent(); + } + return false; + } + private static String printMod(Class c) { String name = c.getName().toLowerCase(); name = name.replaceFirst("^.*\\.", ""); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java index e4fee456e..d99f81a21 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClassesTests.java @@ -574,8 +574,9 @@ public void destroyed() { "package test", " native testSuccess()", " class A", + " int x = 7", " function foo() returns int", - " return 7", + " return x", " init", " A a = new A()", " let b = a", diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java index da56c8e39..0d90935d6 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -9,54 +9,54 @@ public class GenericsWithTypeclassesTests extends WurstScriptTest { @Test public void identity() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity(A a) returns A", - " return a", - " init", - " int x = identity(3)", - " string s = identity(\"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity(A a) returns A", + " return a", + " init", + " int x = identity(3)", + " string s = identity(\"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @Test public void identityTrans() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity1(A a) returns A", - " return a", - " function identity2(B a) returns B", - " return identity1(a)", - " function identity3(C a) returns C", - " return identity2(a)", - " init", - " int x = identity3(3)", - " string s = identity3(\"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity1(A a) returns A", + " return a", + " function identity2(B a) returns B", + " return identity1(a)", + " function identity3(C a) returns C", + " return identity2(a)", + " init", + " int x = identity3(3)", + " string s = identity3(\"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @Test public void identityRec() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity(int i, A a) returns A", - " if i > 0", - " return identity(i - 1, a)", - " return a", - " init", - " int x = identity(5, 3)", - " string s = identity(5, \"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity(int i, A a) returns A", + " if i > 0", + " return identity(i - 1, a)", + " return a", + " init", + " int x = identity(5, 3)", + " string s = identity(5, \"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @@ -64,59 +64,59 @@ public void identityRec() { @Ignore // TODO public void identityRecTypeCreation() { testAssertErrorsLines(true, "some error message", - "package test", - " native testSuccess()", - " class C", - " construct(T t)", - " function blub(int i, A a) returns int", - " if i <= 0", - " return 0", - " return 1 + blub>(i-1, new C(a))", - " init", - " int x = blub(5, 3)", - " if x == 5", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class C", + " construct(T t)", + " function blub(int i, A a) returns int", + " if i <= 0", + " return 0", + " return 1 + blub>(i-1, new C(a))", + " init", + " int x = blub(5, 3)", + " if x == 5", + " testSuccess()", + "endpackage" ); } @Test public void identityRecMut() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity1(int i, A a) returns A", - " if i > 0", - " return identity2(i - 1, a)", - " return a", - " function identity2(int i, A a) returns A", - " if i > 0", - " return identity1(i - 1, a)", - " return a", - " init", - " int x = identity1(5, 3)", - " string s = identity1(5, \"a\")", - " if x == 3 and s == \"a\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity1(int i, A a) returns A", + " if i > 0", + " return identity2(i - 1, a)", + " return a", + " function identity2(int i, A a) returns A", + " if i > 0", + " return identity1(i - 1, a)", + " return a", + " init", + " int x = identity1(5, 3)", + " string s = identity1(5, \"a\")", + " if x == 3 and s == \"a\"", + " testSuccess()", + "endpackage" ); } @Test public void extensionFunc() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function boolean.choice(A x, A y) returns A", - " if this", - " return x", - " return y", - " init", - " int x = true.choice(5, 3)", - " string s = false.choice(\"a\", \"b\")", - " if x == 5 and s == \"b\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function boolean.choice(A x, A y) returns A", + " if this", + " return x", + " return y", + " init", + " int x = true.choice(5, 3)", + " string s = false.choice(\"a\", \"b\")", + " if x == 5 and s == \"b\"", + " testSuccess()", + "endpackage" ); } @@ -124,56 +124,53 @@ public void extensionFunc() { @Test public void extensionFuncReceiver() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function A.choice(boolean b, A y) returns A", - " if b", - " return this", - " return y", - " init", - " int x = (5).choice(true, 3)", - " string s = \"a\".choice(false, \"b\")", - " if x == 5 and s == \"b\"", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function A.choice(boolean b, A y) returns A", + " if b", + " return this", + " return y", + " init", + " int x = (5).choice(true, 3)", + " string s = \"a\".choice(false, \"b\")", + " if x == 5 and s == \"b\"", + " testSuccess()", + "endpackage" ); } @Test public void genericsDispatch() { testAssertOkLines(true, - "package Test", - "native testSuccess()", - "class Cell", - " T o", - "init", - " Cell x = new Cell()", - " Cell y = new Cell()", - " x.o = 3", - " y.o = \"a\"", - " if x.o == 3 and y.o == \"a\"", - " testSuccess()" + "package Test", + "native testSuccess()", + "class Cell", + " T o", + "init", + " Cell x = new Cell()", + " Cell y = new Cell()", + " x.o = 3", + " y.o = \"a\"", + " if x.o == 3 and y.o == \"a\"", + " testSuccess()" ); } - - - @Test public void identity2() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class C", - " function identity(A a) returns A", - " return a", - " init", - " C a = new C()", - " C b = identity(a)", - " if a == b", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class C", + " function identity(A a) returns A", + " return a", + " init", + " C a = new C()", + " C b = identity(a)", + " if a == b", + " testSuccess()", + "endpackage" ); } @@ -181,95 +178,95 @@ public void identity2() { @Test public void function() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class List", - " function iterator() returns Iterator", - " return new Iterator(this)", - " class Iterator", - " S t", - " construct(List t)", - " int x = 1", - " function hasNext() returns boolean", - " return true", - " function next() returns S", - " return t", - " class A", - " class B", - " class C", - " init", - " List a = new List()", -// " for B b in a", - " Iterator iterator = a.iterator()", - " while iterator.hasNext()", - " B b = iterator.next()", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class List", + " function iterator() returns Iterator", + " return new Iterator(this)", + " class Iterator", + " S t", + " construct(List t)", + " int x = 1", + " function hasNext() returns boolean", + " return true", + " function next() returns S", + " return t", + " class A", + " class B", + " class C", + " init", + " List a = new List()", +// " for B b in a", + " Iterator iterator = a.iterator()", + " while iterator.hasNext()", + " B b = iterator.next()", + " testSuccess()", + "endpackage" ); } @Test public void testSubtypeGenericClass() { testAssertOkLines(false, - "package test", - " class A", - " class B extends A", - " init", - " A x = new B", - "endpackage" + "package test", + " class A", + " class B extends A", + " init", + " A x = new B", + "endpackage" ); } @Test public void testSubtypeGenericClass2() { testAssertOkLines(false, - "package test", - " class A", - " class B extends A", - " function foo()", - " A x = new B", - "endpackage" + "package test", + " class A", + " class B extends A", + " function foo()", + " A x = new B", + "endpackage" ); } @Test public void testSubtypeGenericInterface() { testAssertOkLines(false, - "package test", - " interface I", - " class B implements I", - " init", - " I x = new B", - "endpackage" + "package test", + " interface I", + " class B implements I", + " init", + " I x = new B", + "endpackage" ); } @Test public void identityFail1() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " function identity(A a) returns A", - " return a", - " init", - " real x = identity(3.14)", - " if x == 3.14", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " function identity(A a) returns A", + " return a", + " init", + " real x = identity(3.14)", + " if x == 3.14", + " testSuccess()", + "endpackage" ); } @Test public void identityFail2() { testAssertErrorsLines(true, "Cannot assign C to real", - "package test", - " function identity(A a) returns A", - " return a", - " class C", - " int y", - " init", - " real x = identity(new C())", - "endpackage" + "package test", + " function identity(A a) returns A", + " return a", + " class C", + " int y", + " init", + " real x = identity(new C())", + "endpackage" ); } @@ -277,162 +274,162 @@ public void identityFail2() { @Test public void cellExample() { testAssertErrorsLines(true, "Wrong parameter type", - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " class A", - " class B", - " init", - " Cell c = new Cell()", - " c.set(new B())", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " class A", + " class B", + " init", + " Cell c = new Cell()", + " c.set(new B())", + "endpackage" ); } @Test public void implicitConversions() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " if c.get() == bla(5, 3)", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " if c.get() == bla(5, 3)", + " testSuccess()", + "endpackage" ); } @Test public void implicitConversions2() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " c.set(c.get())", - " if c.get() == bla(5, 3)", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " c.set(c.get())", + " if c.get() == bla(5, 3)", + " testSuccess()", + "endpackage" ); } @Test public void implicitConversions3() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " public interface FoldClosure", - " function apply(T t, Q q) returns Q", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " function fold(Q start, FoldClosure f) returns Q", - " return f.apply(elem, start)", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(5, 3))", - " let x = c.fold(2, (e, a) -> e.z + a)", - " if x == 7", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " public interface FoldClosure", + " function apply(T t, Q q) returns Q", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " function fold(Q start, FoldClosure f) returns Q", + " return f.apply(elem, start)", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(5, 3))", + " let x = c.fold(2, (e, a) -> e.z + a)", + " if x == 7", + " testSuccess()", + "endpackage" ); } @Test public void implicitConversions4() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "interface TFunc", - " abstract function run(T t)", - "", - "function runFunc(TFunc func)", - " func.run(false)", - "", - "init", - " runFunc( (bool b) -> begin", - " testSuccess()", - " end )" + "package test", + "native testSuccess()", + "interface TFunc", + " abstract function run(T t)", + "", + "function runFunc(TFunc func)", + " func.run(false)", + "", + "init", + " runFunc( (bool b) -> begin", + " testSuccess()", + " end )" ); } @Test public void implicitConversions5() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "@extern native R2I(real r) returns int", - "@extern native R2S(real r) returns string", - "native println(string s)", - "interface F", - " function apply(A a) returns R", - "class Cell", - " T elem", - " construct(T t)", - " this.elem = t", - " function get() returns T", - " return elem", - " function map(F f) returns Cell", - " return new Cell(f.apply(elem))", - "function real.assertEquals(real expected)", - " if this == expected", - " testSuccess()", - " else", - " println(R2S(this))", - "init", - " let a = new Cell(5)", - " let b = a.map(i -> i*10.)", - " b.get().assertEquals(50)" + "package test", + "native testSuccess()", + "@extern native R2I(real r) returns int", + "@extern native R2S(real r) returns string", + "native println(string s)", + "interface F", + " function apply(A a) returns R", + "class Cell", + " T elem", + " construct(T t)", + " this.elem = t", + " function get() returns T", + " return elem", + " function map(F f) returns Cell", + " return new Cell(f.apply(elem))", + "function real.assertEquals(real expected)", + " if this == expected", + " testSuccess()", + " else", + " println(R2S(this))", + "init", + " let a = new Cell(5)", + " let b = a.map(i -> i*10.)", + " b.get().assertEquals(50)" ); } @Test public void implicitConversionsFail() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - "", - " tuple bla(int z, int y)", - " init", - " Cell c = new Cell()", - " c.set(bla(3,4))", - " if c.get() == bla(3,4)", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + "", + " tuple bla(int z, int y)", + " init", + " Cell c = new Cell()", + " c.set(bla(3,4))", + " if c.get() == bla(3,4)", + " testSuccess()", + "endpackage" ); } @@ -440,157 +437,156 @@ public void implicitConversionsFail() { @Test public void implicitConversionsAssign() { testAssertOkLines(false, - "type unit extends handle", - "package test", - " native testSuccess()", - " class Cell", - " T elem", - " function set(T t)", - " elem = t", - " function get() returns T", - " return elem", - " init", - " Cell c = new Cell()", - " Cell c2 = c", - "endpackage" + "type unit extends handle", + "package test", + " native testSuccess()", + " class Cell", + " T elem", + " function set(T t)", + " elem = t", + " function get() returns T", + " return elem", + " init", + " Cell c = new Cell()", + " Cell c2 = c", + "endpackage" ); } @Test public void nativeTypes() { testAssertOkLines(false, - "type effect extends handle", - "package Test", - "class L", - "init", - " L l = new L()" + "type effect extends handle", + "package Test", + "class L", + "init", + " L l = new L()" ); } - @Test public void implicitConversionFailSimple() { // see bug #121 testAssertErrorsLines(false, "cast expression not defined for expression type int", - "type effect extends handle", - "package Test", + "type effect extends handle", + "package Test", - "class List", - " function get() returns T", - " return 0 castTo T", + "class List", + " function get() returns T", + " return 0 castTo T", - "init", - " List fxs = new List()", - " let f = fxs.get()", - "endpackage"); + "init", + " List fxs = new List()", + " let f = fxs.get()", + "endpackage"); } @Test public void cast() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "class Cell", - " T o", - "class A", - " function foo() returns int", - " return 5", - "class B extends A", - " override function foo() returns int", - " return 6", - "init", - " Cell c = new Cell()", - " c.o = new B()", - " B b = c.o castTo B" + "package Test", + "native testSuccess()", + "class Cell", + " T o", + "class A", + " function foo() returns int", + " return 5", + "class B extends A", + " override function foo() returns int", + " return 6", + "init", + " Cell c = new Cell()", + " c.o = new B()", + " B b = c.o castTo B" ); } @Test public void genericsDispatch2() { testAssertOkLines(true, - "package Test", - "native testSuccess()", - "class Cell", - " T o", - "class A", - " function foo() returns int", - " return 5", - "class B extends A", - " override function foo() returns int", - " return 6", - "init", - " Cell c = new Cell()", - " c.o = new B()", - " if c.o.foo() == 6", - " testSuccess()" + "package Test", + "native testSuccess()", + "class Cell", + " T o", + "class A", + " function foo() returns int", + " return 5", + "class B extends A", + " override function foo() returns int", + " return 6", + "init", + " Cell c = new Cell()", + " c.o = new B()", + " if c.o.foo() == 6", + " testSuccess()" ); } @Test public void genericsSubstitute1() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "class A", - " function bla(T t)", - "class B extends A", - "class C", - "init", - " let b = new B", - " b.bla(new C)" + "package Test", + "native testSuccess()", + "class A", + " function bla(T t)", + "class B extends A", + "class C", + "init", + " let b = new B", + " b.bla(new C)" ); } @Test public void genericsSubstitute2() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "interface I", - " function bla(T t, S s)", - " skip", - "class A implements I", - "class B extends A", - "class C", - "class D", - "init", - " let b = new B", - " b.bla(new D, new C)" + "package Test", + "native testSuccess()", + "interface I", + " function bla(T t, S s)", + " skip", + "class A implements I", + "class B extends A", + "class C", + "class D", + "init", + " let b = new B", + " b.bla(new D, new C)" ); } @Test public void genericsSubstitute3() { testAssertOkLines(false, - "package Test", - "native testSuccess()", - "interface I", - " function bla(T t, S s)", - " skip", - "interface J extends I", - " function foo()", - " skip", - "class A implements J", - "class B extends A", - "class C", - "class D", - "init", - " let b = new B", - " b.bla(new D, new C)" + "package Test", + "native testSuccess()", + "interface I", + " function bla(T t, S s)", + " skip", + "interface J extends I", + " function foo()", + " skip", + "class A implements J", + "class B extends A", + "class C", + "class D", + "init", + " let b = new B", + " b.bla(new D, new C)" ); } @Test public void genericsSubstitute() { testAssertOkLines(false, - "package Test", - "class A", - " function bla(T a)", - "class B extends A", - " function do()", - " bla(new MyType)", - "class MyType" + "package Test", + "class A", + " function bla(T a)", + "class B extends A", + " function do()", + " bla(new MyType)", + "class MyType" ); } @@ -598,13 +594,13 @@ public void genericsSubstitute() { @Test public void genericsSubstitute_override() { testAssertOkLines(false, - "package Test", - "class A", - " function bla(T a)", - "class B extends A", - " override function bla(MyType t)", - " skip", - "class MyType" + "package Test", + "class A", + " function bla(T a)", + "class B extends A", + " override function bla(MyType t)", + " skip", + "class MyType" ); } @@ -613,16 +609,16 @@ public void genericsSubstitute_override() { @Test public void genericsSubstitute_override_interface() { testAssertOkLines(false, - "package Test", - "interface I", - " function bla(S s, T t)", - "interface J extends I", - " function foo(T t)", - "class B implements J", - " override function bla(int s, MyType t)", - " skip", - " override function foo(MyType t)", - "class MyType" + "package Test", + "interface I", + " function bla(S s, T t)", + "interface J extends I", + " function foo(T t)", + "class B implements J", + " override function bla(int s, MyType t)", + " skip", + " override function foo(MyType t)", + "class MyType" ); } @@ -630,16 +626,16 @@ public void genericsSubstitute_override_interface() { @Test public void genericsSubstitute_override_interface_fail() { testAssertErrorsLines(false, "Parameter int s should have type MyType to override function bla", - "package Test", - "interface I", - " function bla(S s, T t)", - "interface J extends I", - " function foo(T t)", - "class B implements J", - " override function bla(int s, MyType t)", - " skip", - " override function foo(MyType t)", - "class MyType" + "package Test", + "interface I", + " function bla(S s, T t)", + "interface J extends I", + " function foo(T t)", + "class B implements J", + " override function bla(int s, MyType t)", + " skip", + " override function foo(MyType t)", + "class MyType" ); } @@ -648,203 +644,203 @@ public void genericsSubstitute_override_interface_fail() { @Test public void genericMethod1() { testAssertOkLines(false, - "package Test", - "class Blub", - "function bla(Blub t)", - "init", - " bla(new Blub)" + "package Test", + "class Blub", + "function bla(Blub t)", + "init", + " bla(new Blub)" ); } @Test public void genericExtensionMethod1() { testAssertOkLines(false, - "package Test", - "class Blub", - "function Blub.bla()", - " skip", - "init", - " new Blub.bla()" + "package Test", + "class Blub", + "function Blub.bla()", + " skip", + "init", + " new Blub.bla()" ); } @Test public void genericReturnOverride() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package Test", - "interface I", - " function f() returns T", - "class C implements I", - " function f() returns T", - " return null" + "package Test", + "interface I", + " function f() returns T", + "class C implements I", + " function f() returns T", + " return null" ); } @Test public void genericReturnOverride2() { testAssertOkLines(false, - "package Test", - "interface I", - " function f(S t) returns S", - "class C implements I", - " function f(T t) returns T", - " return t" + "package Test", + "interface I", + " function f(S t) returns S", + "class C implements I", + " function f(T t) returns T", + " return t" ); } @Test public void genericRecursive() { testAssertOkLines(false, - "package Test", - "public class C", - " C x", - " function foo()", - " this.x.x = null" + "package Test", + "public class C", + " C x", + " function foo()", + " this.x.x = null" ); } @Test public void genericRecursive2() { testAssertOkLines(false, - "package Test", - "public class C", - " C x", - " function foo()", - " C c = new C", - " c.x.x = null" + "package Test", + "public class C", + " C x", + " function foo()", + " C c = new C", + " c.x.x = null" ); } @Test public void genericChain1() { testAssertOkLines(false, - "package Test", - "class A", - "public class C", - " K x", - "init", - " C>> c = null", - " c.x.x.x = new A" + "package Test", + "class A", + "public class C", + " K x", + "init", + " C>> c = null", + " c.x.x.x = new A" ); } @Test public void genericChain1Err() { testAssertErrorsLines(false, "Cannot assign", - "package Test", - "class A", - "public class C", - " K x", - "init", - " C>> c = null", - " c.x.x = new A" + "package Test", + "class A", + "public class C", + " K x", + "init", + " C>> c = null", + " c.x.x = new A" ); } @Test public void genericChain2() { testAssertOkLines(false, - "package Test", - "class A", - "public class C", - " C> x", - "init", - " C c = null", - " c.x.x.x = new C>>>" + "package Test", + "class A", + "public class C", + " C> x", + "init", + " C c = null", + " c.x.x.x = new C>>>" ); } @Test public void genericChain2ErrA() { testAssertErrorsLines(false, "Cannot assign", - "package Test", - "class A", - "public class C", - " C> x", - "init", - " C c = null", - " c.x.x.x = new C>>>>" + "package Test", + "class A", + "public class C", + " C> x", + "init", + " C c = null", + " c.x.x.x = new C>>>>" ); } @Test public void genericChain2ErrB() { testAssertErrorsLines(false, "Cannot assign", - "package Test", - "class A", - "public class C", - " C> x", - "init", - " C c = null", - " c.x.x.x = new C>>" + "package Test", + "class A", + "public class C", + " C> x", + "init", + " C c = null", + " c.x.x.x = new C>>" ); } @Test public void implicitsWithClass() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "interface Comparison", - " function leq(T t, T u) returns bool", - "class BoolComp implements Comparison", - " override function leq(bool a, bool b) returns bool", - " return not a or b", - "Comparison bc = new BoolComp", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "interface Comparison", + " function leq(T t, T u) returns bool", + "class BoolComp implements Comparison", + " override function leq(bool a, bool b) returns bool", + " return not a or b", + "Comparison bc = new BoolComp", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @Test public void implicitsWithClass2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class Comparison", - " function leq(T t, T u) returns bool", - " return true", - "class BoolComp extends Comparison", - " override function leq(bool a, bool b) returns bool", - " return not a or b", - "Comparison bc = new BoolComp", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "class Comparison", + " function leq(T t, T u) returns bool", + " return true", + "class BoolComp extends Comparison", + " override function leq(bool a, bool b) returns bool", + " return not a or b", + "Comparison bc = new BoolComp", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @Test public void implicitsWithClass3() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class Comparison", - " function leq(T t, T u) returns bool", - " return true", - "class ComparisonX extends Comparison", - "class BoolComp extends ComparisonX", - " override function leq(bool a, bool b) returns bool", - " return not a or b", - "ComparisonX bc = new BoolComp", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "class Comparison", + " function leq(T t, T u) returns bool", + " return true", + "class ComparisonX extends Comparison", + "class BoolComp extends ComparisonX", + " override function leq(bool a, bool b) returns bool", + " return not a or b", + "ComparisonX bc = new BoolComp", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @Test public void implicitsWithClosures() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "interface Comparison", - " function leq(T t, T u) returns bool", - "Comparison bc = (bool a, bool b) -> not a or b", - "init", - " if bc.leq(false, true)", - " testSuccess()" + "package test", + "native testSuccess()", + "interface Comparison", + " function leq(T t, T u) returns bool", + "Comparison bc = (bool a, bool b) -> not a or b", + "init", + " if bc.leq(false, true)", + " testSuccess()" ); } @@ -852,25 +848,56 @@ public void implicitsWithClosures() { @Test public void genericForIn() { testAssertErrorsLines(true, "cast expression not defined for expression type int", - "package test", - "native testSuccess()", - "class C", - " function iterator() returns Iterator", - " return new Iterator()", - "class Iterator", - " private int i = 0", - " function next() returns T", - " i = i + 1", - " return i castTo T", - " function hasNext() returns boolean", - " return i < 10", - " function close()", - " destroy this", - "init", - " let c = new C", - " for i in c", - " if i == 5", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function iterator() returns Iterator", + " return new Iterator()", + "class Iterator", + " private int i = 0", + " function next() returns T", + " i = i + 1", + " return i castTo T", + " function hasNext() returns boolean", + " return i < 10", + " function close()", + " destroy this", + "init", + " let c = new C", + " for i in c", + " if i == 5", + " testSuccess()" + ); + } + + @Test + public void genericForIn2() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "class C", + " T x", + " construct(T x)", + " this.x = x", + " function iterator() returns Iterator", + " return new Iterator(x)", + "class Iterator", + " private int i = 0", + " T x", + " construct(T x)", + " this.x = x", + " function next() returns T", + " i = i + 1", + " return x", + " function hasNext() returns boolean", + " return i < 1", + " function close()", + " destroy this", + "init", + " let c = new C(\"42\")", + " for i in c", + " if i == \"42\"", + " testSuccess()" ); } @@ -878,164 +905,164 @@ public void genericForIn() { @Ignore public void genericForFrom() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " function iterator() returns Iterator", - " return new Iterator()", - "class Iterator", - " private int i = 0", - " function next() returns T", - " i = i + 1", - " return i castTo T", - " function hasNext() returns boolean", - " return i < 10", - "init", - " let c = new C", - " let iter = c.iterator()", - " for i from iter", - " if i == 5", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function iterator() returns Iterator", + " return new Iterator()", + "class Iterator", + " private int i = 0", + " function next() returns T", + " i = i + 1", + " return i castTo T", + " function hasNext() returns boolean", + " return i < 10", + "init", + " let c = new C", + " let iter = c.iterator()", + " for i from iter", + " if i == 5", + " testSuccess()" ); } @Test public void genericOverload() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " private T x", - " construct(T x)", - " this.x = x", - " function foo(T t)", - " foo(new C(t))", - " function foo(C t)", - " testSuccess()", - "init", - " let c = new C(1)", - " c.foo(1)" + "package test", + "native testSuccess()", + "class C", + " private T x", + " construct(T x)", + " this.x = x", + " function foo(T t)", + " foo(new C(t))", + " function foo(C t)", + " testSuccess()", + "init", + " let c = new C(1)", + " c.foo(1)" ); } @Test public void genericOverload2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " private T x", - " construct(T x)", - " this.x = x", - " function foo(T t)", - " foo(new C(t))", - " function foo(C t)", - " testSuccess()", - " function test()", - " let c = new C(1)", - " c.foo(1)", - "init", - " new C(1).test()" + "package test", + "native testSuccess()", + "class C", + " private T x", + " construct(T x)", + " this.x = x", + " function foo(T t)", + " foo(new C(t))", + " function foo(C t)", + " testSuccess()", + " function test()", + " let c = new C(1)", + " c.foo(1)", + "init", + " new C(1).test()" ); } @Test public void inferType() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "function id(int x) returns int", - " return x", - "class C", - " var x = id(4)", - "init", - " let x= new C", - " if x.x == 4", - " testSuccess()" + "package test", + "native testSuccess()", + "function id(int x) returns int", + " return x", + "class C", + " var x = id(4)", + "init", + " let x= new C", + " if x.x == 4", + " testSuccess()" ); } @Test public void simpleFunctionCall() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " function foo() returns int", - " return 4", - "init", - " let x = new C", - " if x.foo() == 4", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function foo() returns int", + " return 4", + "init", + " let x = new C", + " if x.foo() == 4", + " testSuccess()" ); } @Test public void simpleFunctionCall2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "class C", - " function foo() returns int", - " return bar()", - " function bar() returns int", - " return 4", - "init", - " let x = new C", - " if x.foo() == 4", - " testSuccess()" + "package test", + "native testSuccess()", + "class C", + " function foo() returns int", + " return bar()", + " function bar() returns int", + " return 4", + "init", + " let x = new C", + " if x.foo() == 4", + " testSuccess()" ); } @Test public void genericFunctionOverload() { // #628 testAssertOkLines(false, - "package test", - "native testSuccess()", - "class LinkedList", - " T x", - "public function LinkedList.foo(string separator) returns string", - " this.foo(s -> s, separator) // Doesn't work", - " this.foo2(s -> s, separator) // Works", - " return separator", - "interface ToStringClosure", - " function apply(A a) returns A", - "public function LinkedList.foo(ToStringClosure cls, string separator)", - "public function LinkedList.foo2(ToStringClosure cls, string separator)", - "init", - " let x = new LinkedList", - " x.foo(\"a\")" + "package test", + "native testSuccess()", + "class LinkedList", + " T x", + "public function LinkedList.foo(string separator) returns string", + " this.foo(s -> s, separator) // Doesn't work", + " this.foo2(s -> s, separator) // Works", + " return separator", + "interface ToStringClosure", + " function apply(A a) returns A", + "public function LinkedList.foo(ToStringClosure cls, string separator)", + "public function LinkedList.foo2(ToStringClosure cls, string separator)", + "init", + " let x = new LinkedList", + " x.foo(\"a\")" ); } @Test public void extensionFunc2() { // #718 testAssertOkLines(false, - "package test", - "native testSuccess()", - "public function T.foo() returns T", - " return this", - "init", - " let x = \"hello\".foo()", - " if x == \"hello\"", - " testSuccess()" + "package test", + "native testSuccess()", + "public function T.foo() returns T", + " return this", + "init", + " let x = \"hello\".foo()", + " if x == \"hello\"", + " testSuccess()" ); } @Test public void strangeFoldl() { // #655 testAssertOkLines(false, - "package test", - "native testSuccess()", - "class LinkedList", - " T x", - " function foldl(Q startValue, FoldClosure predicate) returns Q", - " return startValue", - "interface FoldClosure", - " function run(X t, Y q) returns Y", - "init", - " let x = new LinkedList", - " x.foldl(0, (x, y) -> x + y)" + "package test", + "native testSuccess()", + "class LinkedList", + " T x", + " function foldl(Q startValue, FoldClosure predicate) returns Q", + " return startValue", + "interface FoldClosure", + " function run(X t, Y q) returns Y", + "init", + " let x = new LinkedList", + " x.foldl(0, (x, y) -> x + y)" ); } @@ -1043,57 +1070,57 @@ public void strangeFoldl() { // #655 @Ignore public void normalFoldlInfer() { // #657 testAssertOkLines(true, - "package test", - "native testSuccess()", - "@extern native I2S(int i) returns string", - "string array s", - "int s_max = -1", - "class LinkedList", - " T x", - " function foldl(Q startValue, FoldClosure predicate) returns Q", - " return predicate.run(x, startValue)", - " function toString() returns string", - " let fold = foldl(\"[\", (i, q) -> q + I2S(i castTo int) + \",\")", - " return fold + \"]\"", - "interface FoldClosure", - " function run(T t, Q q) returns Q", - "init", - " let x = new LinkedList", - " x.x = 5", - " if x.toString() == \"[5,]\"", - " testSuccess()" + "package test", + "native testSuccess()", + "@extern native I2S(int i) returns string", + "string array s", + "int s_max = -1", + "class LinkedList", + " T x", + " function foldl(Q startValue, FoldClosure predicate) returns Q", + " return predicate.run(x, startValue)", + " function toString() returns string", + " let fold = foldl(\"[\", (i, q) -> q + I2S(i castTo int) + \",\")", + " return fold + \"]\"", + "interface FoldClosure", + " function run(T t, Q q) returns Q", + "init", + " let x = new LinkedList", + " x.x = 5", + " if x.toString() == \"[5,]\"", + " testSuccess()" ); } @Test public void inheritField() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class A", - " int someInt", - "class B extends A", - " construct()", - " someInt = 1", - "init", - " if new B().someInt == 1", - " testSuccess()" + "package test", + "native testSuccess()", + "abstract class A", + " int someInt", + "class B extends A", + " construct()", + " someInt = 1", + "init", + " if new B().someInt == 1", + " testSuccess()" ); } @Test public void inheritField2() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class A", - " int someInt", - "class B extends A", - " construct()", - " someInt = 1", - "init", - " if new B().someInt == 1", - " testSuccess()" + "package test", + "native testSuccess()", + "abstract class A", + " int someInt", + "class B extends A", + " construct()", + " someInt = 1", + "init", + " if new B().someInt == 1", + " testSuccess()" ); } @@ -1101,63 +1128,63 @@ public void inheritField2() { @Test public void inheritMethod() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class A", - " function someInt()", - " testSuccess()", - "class B extends A", - " construct()", - " someInt()", - "init", - " new B()" + "package test", + "native testSuccess()", + "abstract class A", + " function someInt()", + " testSuccess()", + "class B extends A", + " construct()", + " someInt()", + "init", + " new B()" ); } @Test public void nullWithGeneric() { testAssertErrorsLines(false, "Cannot compare types T with null", - "package test", - "native testSuccess()", - "function foo(T t)", - " if t == null", - " testSuccess()", - "init", - " foo(null)" + "package test", + "native testSuccess()", + "function foo(T t)", + " if t == null", + " testSuccess()", + "init", + " foo(null)" ); } @Test public void missingTypeArgsFunc() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "function foo() returns T", - " return null", - "init", - " let x = foo()" + "package test", + "function foo() returns T", + " return null", + "init", + " let x = foo()" ); } @Test public void missingTypeArgsMethod() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "class C", - " function foo() returns T", - " return null", - "init", - " let c = new C", - " let x = c.foo()" + "package test", + "class C", + " function foo() returns T", + " return null", + "init", + " let c = new C", + " let x = c.foo()" ); } @Test public void missingTypeArgsConstructor() { testAssertErrorsLines(false, "Cannot infer type for type parameter T", - "package test", - "class C", - "init", - " let c = new C" + "package test", + "class C", + "init", + " let c = new C" ); } @@ -1165,113 +1192,787 @@ public void missingTypeArgsConstructor() { @Test public void tooManyTypeArgsFunc() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "function foo() returns T", - " return null", - "init", - " let x = foo()" + "package test", + "function foo() returns T", + " return null", + "init", + " let x = foo()" ); } @Test public void tooManyTypeArgsMethod() { testAssertErrorsLines(false, "Cannot return null, expected expression of type T", - "package test", - "class C", - " function foo() returns T", - " return null", - "init", - " let c = new C", - " let x = c.foo()" + "package test", + "class C", + " function foo() returns T", + " return null", + "init", + " let c = new C", + " let x = c.foo()" ); } @Test public void tooManyTypeArgsConstructor() { testAssertErrorsLines(false, "Too many type arguments given", - "package test", - "class C", - "init", - " let c = new C" + "package test", + "class C", + "init", + " let c = new C" ); } @Test public void capturedType() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "native println(string s)", - "interface F", - " function apply(A a) returns R", - "function twice(F f) returns F", - " return x -> f.apply(f.apply(x))", // line 7 - "init", - " F plus1 = x -> x + 1", // line 9 - " F plus2 = twice(plus1)", - " F shout = twice((string s) -> s + \"!\")", // line 11 - " if shout.apply(\"hello\") == \"hello!!\" and plus2.apply(1) == 3", - " testSuccess()" + "package test", + "native testSuccess()", + "native println(string s)", + "interface F", + " function apply(A a) returns R", + "function twice(F f) returns F", + " return x -> f.apply(f.apply(x))", // line 7 + "init", + " F plus1 = x -> x + 1", // line 9 + " F plus2 = twice(plus1)", +// " F shout = twice((string s) -> s + \"!\")", // line 11 + " if plus2.apply(1) == 3", + " testSuccess()" ); } @Test public void severalSubMethods() { // #490 testAssertOkLines(true, - "package test", - "native testSuccess()", - "native println(string s)", - "class B", - " function id(T x) returns T", - " return x", - "class A extends B", - " override function id(Y y) returns Y", - " return y", - "init", - " B a = new A", - " B b = new A", - " if a.id(4) == 4 and b.id(2) == 2", - " testSuccess()" + "package test", + "native testSuccess()", + "native println(string s)", + "class B", + " function id(T x) returns T", + " return x", + "class A extends B", + " override function id(Y y) returns Y", + " return y", + "init", + " B a = new A", + " B b = new A", + " if a.id(4) == 4 and b.id(2) == 2", + " testSuccess()" ); } @Test public void simpleCastTest() { testAssertOkLines(true, - "package test", - " native testSuccess()", - " class A", - " class B extends A", - " int x", - " function get() returns A", - " let r = new B", - " r.x = 5", - " return r", - " init", - " if (get() castTo B).x == 5", - " testSuccess()", - "endpackage" + "package test", + " native testSuccess()", + " class A", + " class B extends A", + " int x", + " function get() returns A", + " let r = new B", + " r.x = 5", + " return r", + " init", + " if (get() castTo B).x == 5", + " testSuccess()", + "endpackage" ); } @Test public void abstractReturnT() { testAssertOkLines(true, - "package test", - "native testSuccess()", - "abstract class F", - " abstract function get() returns T", - "class X extends F", - " T t", - " construct(T t)", - " this.t = t", - " override function get() returns T", - " return t", - "init", - " F x = new X(42)", - " if x.get() == 42", - " testSuccess()" + "package test", + "native testSuccess()", + "abstract class F", + " abstract function get() returns T", + "class X extends F", + " T t", + " construct(T t)", + " this.t = t", + " override function get() returns T", + " return t", + "init", + " F x = new X(42)", + " if x.get() == 42", + " testSuccess()" + ); + } + + + @Test + public void typeClassMustBeInterface1() { + testAssertErrorsLines(false, "Could not find type ToIndex", + "package test", + "native testSuccess()", + "function foo(T x)", + " testSuccess()" + ); + } + + @Test + public void typeClassMustBeInterface2() { + testAssertErrorsLines(false, "Invalid type constraint ToIndex. Type constraint must be an interface type.", + "package test", + "native testSuccess()", + "tuple ToIndex(int x)", + "function foo(T x)", + " testSuccess()" + ); + } + + @Test + public void callMustSatisfyConstraint() { + testAssertErrorsLines(false, "Type integer does not satisfy constraint T: ToIndex", + "package test", + "native testSuccess()", + "interface ToIndex", + " function toIndex(T x) returns int", + "function foo(T x)", + " testSuccess()", + "init", + " foo(42)" + ); + } + + @Test + public void simpleTypeClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ToIndex", + " function toIndex(T x) returns int", // 5 + "class A", + "implements ToIndex", + " function toIndex(A x) returns int", + " return 42", + "function foo(Q x)", // 10 + " if Q.toIndex(x) == 42", + " testSuccess()", + "init", + " let a = new A", + " foo(a)" + ); + } + + + @Test + public void twoTypeClasses() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface Plus", + " function plus(T x, T y) returns T", + "interface Times", + " function times(T x, T y) returns T", + "implements Plus", + " function plus(int x, int y) returns int", + " return x + y", + "implements Times", + " function times(int x, int y) returns int", + " return x * y", + "function calc(Q x) returns Q", + " return Q.plus(x, Q.times(x, x))", + "init", + " if calc(6) == 42", + " testSuccess()" + ); + } + + @Test + public void forwardTypeClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "interface ToIndex", + " function toIndex(T elem) returns int", + "class A", + "implements ToIndex", + " function toIndex(A x) returns int", + " return 42", + "function foo(T x)", + " if T.toIndex(x) == 42", + " testSuccess()", + "function bar(T x)", + " foo(x)", + "init", + " let a = new A", + " bar(a)" + ); + } + + @Test + public void subTypeClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface A", + " function x(T x) returns int", + "interface B extends A", + " function y(T x) returns int", + "class C", + "implements B", + " function x(C x) returns int", + " return 42", + " function y(C x) returns int", + " return 43", + "function foo(Q x)", + " if Q.x(x) == 42", + " testSuccess()", + "init", + " let c = new C", + " foo(c)" + ); + } + + @Test + public void typeClassAmbiguous() { + testAssertErrorsLines(true, "There are multiple instances", + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface A", + " function x(T x) returns int", + "interface B extends A", + " function y(T x) returns int", + "class C", + "implements A", + " function x(C x) returns int", + " return 17", + "implements B", + " function x(C x) returns int", + " return 42", + " function y(C x) returns int", + " return 43", + "function foo(Q x)", + " if Q.x(x) == 42", + " testSuccess()", + "init", + " let c = new C", + " foo(c)" + ); + } + + + @Test + public void buildInstance() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface A", + " function x(X x) returns int", + "interface B", + " function y(Y y) returns int", + "class C", + "implements A", + " function x(C c) returns int", + " return 41", + "implements B for T: A", + " function y(T t) returns int", + " return T.x(t) + 1", + "function foo(Q q)", + " if Q.y(q) == 42", + " testSuccess()", + "init", + " let c = new C", + " foo(c)" + ); + } + + @Test + public void classParameterConstraint() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "init", + " let c = new C", + " c.set(\"42\")", + " if c.get() == 42", + " testSuccess()" + ); + } + + @Test + public void classParameterConstraintBuild() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "class Cell", + " T elem", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "implements ToInt> for T: ToInt", + " function toInt(Cell c) returns int", + " return T.toInt(c.elem)", + "init", + " let cell = new Cell", + " cell.elem = \"42\"", + " let c = new C>", + " c.set(cell)", + " if c.get() == 42", + " testSuccess()" + ); + } + + @Test + public void subtypeConstraint1() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "class D extends C", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "init", + " let d = new D", + " d.set(\"42\")", + " if d.get() == 42", + " testSuccess()" + ); + } + + @Test + public void subtypeConstraintDependent() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface ToInt", + " function toInt(X x) returns int", + "class C", + " int elem", + " function set(T e)", + " this.elem = T.toInt(e)", + " function get() returns int", + " return elem", + "class D extends C", + "class Cell", + " T elem", + "implements ToInt", + " function toInt(string s) returns int", + " return S2I(s)", + "implements ToInt> for T: ToInt", + " function toInt(Cell c) returns int", + " return T.toInt(c.elem)", + "init", + " let d = new D>", + " let c = new Cell", + " c.elem = \"42\"", + " d.set(c)", + " if d.get() == 42", + " testSuccess()" + ); + } + + @Test + public void iteratorExample() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native S2I(string s) returns int", + "interface Iterable, T:>", +// "interface Iterable", + " function iterator(T t) returns I", + "interface Iterator", + " function hasNext(I i) returns bool", + " function next(I i) returns T", + "interface Group", + " function zero() returns T", + " function plus(T a, T b) returns T", + "function sum, I: Iterator>(C coll) returns T", + " var res = T.zero()", + " let iter = C.iterator(coll)", + " while I.hasNext(iter)", + " res = T.plus(res, I.next(iter))", + " return res", + "implements Group", + " function zero() returns int", + " return 0", + " function plus(int a, int b) returns int", + " return a + b", + "class List", + " T elem", + " List next", + " construct(T elem, List next)", + " this.elem = elem", + " this.next = next", + "class ListIterator", + " List current", + " construct(List list)", + " this.current = list", + "implements Iterator> for T", + " function hasNext(ListIterator iter) returns bool", + " return iter.current != null", + " function next(ListIterator iter) returns T", + " let res = iter.current.elem", + " iter.current = iter.current.next", + " return res", + "implements Iterable, List> for E", + " function iterator(List list) returns ListIterator", + " return new ListIterator(list)", + "init", + " let list = new List(100, new List(20, new List(3, null)))", +// " if sum(list) == 123", + " if sum, ListIterator>(list) == 123", + " testSuccess()" + ); + } + + @Test + public void missingFunction() { + testAssertErrorsLines(false, "must implement the following functions", + "package test", + "interface Blub", + " function foo(T x) returns int", + " function bar(T x) returns int", + "class A", + "implements Blub", + " function foo(A x) returns int", + " return 42" + ); + } + + + @Test + public void cyclicImplements() { + testAssertErrorsLines(false, "Type A does not satisfy constraint Y: Apfel", + "package test", + "interface Apfel", + " function a(T x) returns int", + "interface Birne", + " function b(T x) returns int", + "class A", + "implements Apfel for X: Birne", + " function a(X t) returns int", + " return X.b(t)", + "implements Birne for Y: Apfel", + " function b(Y t) returns int", + " return Y.a(t)", + "function foo(T t)", + "init", + " let a = new A()", + " foo(a)" + ); + } + + @Test + public void anyRefClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface AnyRef", + " function toIndex(T x) returns int", + " function fromIndex(int index) returns T", + "class A", + "class Cell", + " int x", + " function set(T x)", + " this.x = T.toIndex(x)", + " function get() returns T", + " return T.fromIndex(this.x)", + "init", + " let a = new A", + " let c = new Cell", + " c.set(a)", + " if c.get() == a", + " testSuccess()" + ); + } + + @Test + public void toIndexFromIndexClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int index) returns T", + "class A", + "class Cell", + " int x", + " function set(T x)", + " this.x = T.toIndex(x)", + " function get() returns T", + " return T.fromIndex(this.x)", + "init", + " let a = new A", + " let c = new Cell", + " c.set(a)", + " if c.get() == a", + " testSuccess()" + ); + } + + @Test + public void defaultForClass() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface Default", + " function defaultValue() returns T", + "class A", + "function d() returns X", + " return X.defaultValue()", + "implements Default", + " function defaultValue() returns int", + " return 42", + "init", + " let a = d()", + " let i = d()", + " if a == null and i == 42", + " testSuccess()" + ); + } + + @Test + public void overloadMiss() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native I2S(int i) returns string", + "interface Show", + " function toString(T t) returns string", + "function T.toString() returns string", + " return T.toString(this)", + "function int.toString() returns string", + " return I2S(this)", + "init", + " if 42 .toString() == \"42\"", + " testSuccess()" + ); + } + + @Test + public void overloadMiss2() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "@extern native I2S(int i) returns string", + "interface Show", + " function toString(T t) returns string", + "function T.toString() returns string", + " return T.toString(this)", + "function int.toString() returns string", + " return I2S(this)", + "implements Show", + " function toString(int i) returns string", + " return i.toString()", + "init", + " if 42 .toString() == \"42\"", + " testSuccess()" ); } + + @Test + public void typeClassFindSupertypeInstance() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + + @Test + public void typeClassExtendedInstance() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + + @Test + public void dependentTypeClassSub() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return bar(x)", + "function bar(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + + @Test + public void instanceViaPublicImport() { + testAssertOkLines(true, + "package a", + "public interface ToIndex", + " function toIndex(T x) returns int", + "public implements ToIndex", + " function toIndex(int x) returns int", + " return x", + "endpackage", + "package b", + "import public a", + "endpackage", + "package test", + "import b", + "native testSuccess()", + "function foo(Q x)", + " if Q.toIndex(x) == 42", + " testSuccess()", + "init", + " foo(42)" + ); + } + + + @Test + public void chainTypeClassesConvertIndex() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + " function toIndex(T x) returns int", + " function fromIndex(int i) returns T", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A", + "implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(Q x) returns int", + " return bar(x)", + "function bar(Q x) returns int", + " return Q.toIndex(x)", + "init", + " let a = new A", + " if foo(\"x\") == 42 and foo(a) != 42", + " testSuccess()" + ); + } + + @Test + public void subInterface() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "native testFail(string s)", + "interface ConvertIndex extends ToIndex, FromIndex", + " function toIndex(T x) returns int", + " function fromIndex(int i) returns T", + "interface ToIndex", + " function toIndex(T x) returns int", + "interface FromIndex", + " function fromIndex(int i) returns T", + "class A implements ConvertIndex", + " function toIndex(string x) returns int", + " return 42", + " function fromIndex(int i) returns string", + " return \"42\"", + "function foo(ConvertIndex c, string x) returns int", + " return bar(c, x)", + "function bar(ToIndex c, string x) returns int", + " return c.toIndex(x)", + "init", + " let a = new A", + " if foo(a, \"x\") == 42", + " testSuccess()" + ); + } } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java index fbb3d9c4b..25c2efdc2 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/NewFeatureTests.java @@ -510,6 +510,7 @@ public void instanceCount() { "native testSuccess()", "native instanceCount(int typeId) returns int", "class A", + " int x = 42", "init", " let a = new A", " let b = new A", @@ -520,7 +521,7 @@ public void instanceCount() { " destroy a", " destroy e", " let count2 = instanceCount(A.typeId)", - " if count1 == 5 and count2 == 3", + " if count1 == 5 and count2 == 3 and b.x == 42", " testSuccess()" ); } @@ -534,6 +535,7 @@ public void instanceMaxCount() { "native testSuccess()", "native maxInstanceCount(int typeId) returns int", "class A", + " int x = 42", "init", " let a = new A", " let b = new A", @@ -544,7 +546,7 @@ public void instanceMaxCount() { " destroy a", " destroy e", " let count2 = maxInstanceCount(A.typeId)", - " if count1 == 5 and count2 == 5", + " if count1 == 5 and count2 == 5 and b.x == 42", " testSuccess()" ); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java index ec92ddb1c..62332e06e 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java @@ -21,8 +21,6 @@ import java.io.File; import java.io.IOException; import java.util.Collections; -import java.util.HashMap; -import java.util.List; import java.util.Map; import static org.testng.AssertJUnit.*; @@ -1033,11 +1031,11 @@ public void localMergerLiveness() throws IOException { LocalMerger localMerger = new LocalMerger(); Element trace = Ast.NoExpr(); - ImVar a = JassIm.ImVar(trace, TypesHelper.imInt(), "a", false); - ImVar b = JassIm.ImVar(trace, TypesHelper.imInt(), "b", false); - ImVar c = JassIm.ImVar(trace, TypesHelper.imInt(), "c", false); - ImVar d = JassIm.ImVar(trace, TypesHelper.imInt(), "d", false); - ImVar e = JassIm.ImVar(trace, TypesHelper.imInt(), "e", false); + ImVar a = JassIm.ImVar(trace, TypesHelper.imInt(), "a", Collections.emptyList()); + ImVar b = JassIm.ImVar(trace, TypesHelper.imInt(), "b", Collections.emptyList()); + ImVar c = JassIm.ImVar(trace, TypesHelper.imInt(), "c", Collections.emptyList()); + ImVar d = JassIm.ImVar(trace, TypesHelper.imInt(), "d", Collections.emptyList()); + ImVar e = JassIm.ImVar(trace, TypesHelper.imInt(), "e", Collections.emptyList()); ImVars locals = JassIm.ImVars(a,b,c,d,e); ImStmts body = JassIm.ImStmts( @@ -1066,9 +1064,9 @@ public void testFunctionSplitter() { prog.getFunctions().add(func); for (int i = 0; i < 10000; i++) { - ImVar l = JassIm.ImVar(model, TypesHelper.imInt(), "l" + i, false); + ImVar l = JassIm.ImVar(model, TypesHelper.imInt(), "l" + i, Collections.emptyList()); func.getLocals().add(l); - ImVar g = JassIm.ImVar(model, TypesHelper.imInt(), "g" + i, false); + ImVar g = JassIm.ImVar(model, TypesHelper.imInt(), "g" + i, Collections.emptyList()); prog.getGlobals().add(g); func.getBody().add(JassIm.ImSet(model, JassIm.ImVarAccess(l), JassIm.ImIntVal(i))); func.getBody().add(JassIm.ImSet(model, JassIm.ImVarAccess(g), JassIm.ImVarAccess(l))); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java index 9d8342bf3..08ab20217 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java @@ -22,6 +22,7 @@ import de.peeeq.wurstscript.jassinterpreter.TestFailException; import de.peeeq.wurstscript.jassinterpreter.TestSuccessException; import de.peeeq.wurstscript.jassprinter.JassPrinter; +import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.translation.lua.translation.LuaTranslator; import de.peeeq.wurstscript.luaAst.LuaCompilationUnit; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; @@ -59,7 +60,7 @@ class TestConfig { private List additionalCompilationUnits = new ArrayList<>(); private boolean stopOnFirstError = true; private boolean runCompiletimeFunctions; - private boolean testLua = false; + private boolean testLua = true; TestConfig(String name) { this.name = name; @@ -116,7 +117,26 @@ TestConfig expectError(String expectedError) { CompilationResult lines(String... lines) { String testName = UtilsIO.getMethodName(WurstScriptTest.class.getName()); additionalCompilationUnits.add(new CU(testName, Utils.join(lines, "\n") + "\n")); - return run(); + try { + return run(); + } catch (CompileError e) { + WPos source = e.getSource(); + if (source.getFile().equals(testName) + && source.getLine() >= 1 + && source.getLine() <= lines.length) { + StringBuilder msg = new StringBuilder(); + for (int i = Math.max(0, source.getLine() - 3); i < source.getLine(); i++) { + msg.append(lines[i]).append("\n"); + } + msg.append(Utils.repeat(' ', source.getStartColumn() - 1)); + msg.append(Utils.repeat('^', source.getEndColumn() - source.getStartColumn())); + msg.append("\n"); + msg.append(e.getMessage()); + throw new CompileError(source, msg.toString(), e.getErrorType(), e); + } else { + throw e; + } + } } CompilationResult compilationUnits(CU... units) { @@ -133,7 +153,7 @@ CompilationResult run() { } else { List errors = res.getGui().getErrorList(); if (errors.stream() - .noneMatch(e -> e.getMessage().toLowerCase().contains(expectedError.toLowerCase()))) { + .noneMatch(e -> e.getMessage().toLowerCase().contains(expectedError.toLowerCase()))) { for (CompileError error : errors) { System.err.println("Unexpected error:" + error); } @@ -347,15 +367,15 @@ private void testWithInliningAndOptimizations(String name, boolean executeProg, } private void testWithInliningAndOptimizationsAndStacktraces(String name, boolean executeProg, boolean executeTests, WurstGui gui, - WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms, RunArgs runArgs) throws Error { + WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms, RunArgs runArgs) throws Error { // test with inlining and local optimization compiler.setRunArgs(runArgs.with("-inline", "-localOptimizations", "-stacktraces")); translateAndTest(name + "_stacktraceinlopt", executeProg, executeTests, gui, compiler, model, executeProgOnlyAfterTransforms); } private void testWithInlining(String name, boolean executeProg, boolean executeTests, WurstGui gui - , WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms - , RunArgs runArgs) throws Error { + , WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms + , RunArgs runArgs) throws Error { // test with inlining compiler.setRunArgs(runArgs.with("-inline")); translateAndTest(name + "_inl", executeProg, executeTests, gui, compiler, model, executeProgOnlyAfterTransforms); @@ -370,7 +390,7 @@ private void testWithLocalOptimizations(String name, boolean executeProg, boolea private void testWithoutInliningAndOptimization(String name, boolean executeProg, boolean executeTests, WurstGui gui, WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms, RunArgs runArgs) - throws Error { + throws Error { compiler.setRunArgs(runArgs); // test without inlining and optimization translateAndTest(name, executeProg, executeTests, gui, compiler, model, executeProgOnlyAfterTransforms); @@ -447,6 +467,7 @@ private void translateAndTestLua(String name, boolean executeProg, WurstGui gui, private void translateAndTest(String name, boolean executeProg, boolean executeTests, WurstGui gui, WurstCompilerJassImpl compiler, WurstModel model, boolean executeProgOnlyAfterTransforms) throws Error { + compiler.clear(); ImProg imProg = compiler.translateProgToIm(model); @@ -504,8 +525,8 @@ WurstModel parseFiles(Iterable inputFiles, Map inputs, boolean withStdLib, WurstCompilerJassImpl compiler) { List inputList = inputs.entrySet().stream() - .map(e -> new CU(e.getKey(), e.getValue())) - .collect(Collectors.toList()); + .map(e -> new CU(e.getKey(), e.getValue())) + .collect(Collectors.toList()); return parseFiles(inputFiles, inputList, withStdLib, compiler); } @@ -553,7 +574,7 @@ private void executeImProg(WurstGui gui, ImProg imProg) throws TestFailException } private void executeJassProg(JassProg prog) - throws TestFailException { + throws TestFailException { try { // run the interpreter with the optimized program JassInterpreter interpreter = new JassInterpreter(); @@ -571,7 +592,7 @@ private void executeTests(WurstGui gui, ImTranslator translator, ImProg imProg) RunTests.TestResult res = runTests.runTests(translator, imProg, null, null); if (res.getPassedTests() < res.getTotalTests()) { throw new Error("tests failed: " + res.getPassedTests() + " / " + res.getTotalTests() + "\n" + - gui.getErrors()); + gui.getErrors()); } }