diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index e6894bff3..0f063cefb 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -70,7 +70,7 @@ dependencies { antlr "org.antlr:antlr4:4.7" // tool for generating AST-classes - compileOnly 'com.github.peterzeller:abstractsyntaxgen:0.3.1' + compileOnly 'com.github.peterzeller:abstractsyntaxgen:59168ba9d61d89c775a53d54b3b83a99f48a585b' // JUnit for testing testCompile group: 'org.testng', name: 'testng', version: '6.14.3' diff --git a/de.peeeq.wurstscript/deploy.gradle b/de.peeeq.wurstscript/deploy.gradle index 0f67fc962..dbd1c06f2 100644 --- a/de.peeeq.wurstscript/deploy.gradle +++ b/de.peeeq.wurstscript/deploy.gradle @@ -41,34 +41,33 @@ create_zip_wurstpack_compiler.dependsOn(make_for_wurstpack) task create_zips { doLast { - mkdir("../downloads/") - copy { - from 'build/distributions/' - into '../downloads/' - } - copy { - from 'build/install/wurstscript/lib/wurstscript.jar' - into '../downloads/' - } - copy { - from '../Wurstpack' - into '../downloads/Wurstpack/' - } - copy { - from '../WurstSetup/build/libs/WurstSetup.jar' - into '../downloads/' - } - // create checksums - mkdir("../Checksums/bin") + mkdir("../downloads/") + copy { + from 'build/distributions/' + into '../downloads/' + } + copy { + from 'build/install/wurstscript/lib/wurstscript.jar' + into '../downloads/' + } + copy { + from '../Wurstpack' + into '../downloads/Wurstpack/' + } + copy { + from '../WurstSetup/build/libs/WurstSetup.jar' + into '../downloads/' + } + // create checksums + mkdir("../Checksums/bin") - javaexec { - classpath = sourceSets.main.runtimeClasspath - main = "de.peeeq.wurstio.Checksums" - args = ["../downloads/Wurstpack/", - "../downloads/wurstpack.md5"] - } + javaexec { + classpath = sourceSets.main.runtimeClasspath + main = "de.peeeq.wurstio.Checksums" + args = ["../downloads/Wurstpack/", + "../downloads/wurstpack.md5"] + } } - } create_zips.dependsOn(installDist) create_zips.dependsOn(create_zip_wurstpack_complete) @@ -76,16 +75,16 @@ create_zips.dependsOn(create_zip_wurstpack_compiler) task generate_hotdoc { doLast { - copy { - from("/src/main/resources/") - into("/build/classes/main/") - } - javaexec { - classpath = sourceSets.main.runtimeClasspath - main = "de.peeeq.wurstio.Main" - args = ["--hotdoc", - "./build/deps/", - "../downloads/hotdoc"] + copy { + from("/src/main/resources/") + into("/build/classes/main/") + } + javaexec { + classpath = sourceSets.main.runtimeClasspath + main = "de.peeeq.wurstio.Main" + args = ["--hotdoc", + "./build/deps/", + "../downloads/hotdoc"] } } } diff --git a/de.peeeq.wurstscript/parserspec/jass_im.parseq b/de.peeeq.wurstscript/parserspec/jass_im.parseq index ed498a026..5e467463a 100644 --- a/de.peeeq.wurstscript/parserspec/jass_im.parseq +++ b/de.peeeq.wurstscript/parserspec/jass_im.parseq @@ -6,13 +6,16 @@ abstract syntax: ImProg( @ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImVars globals, - ImFunctions functions, - ImClasses classes, + 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) @@ -22,25 +25,42 @@ ImType = | ImArrayTypeMulti(ref ImType entryType, java.util.List arraySize) | ImTupleType(java.util.List types, java.util.List names) | ImVoid() - + | ImClassType(ref ImClass classDef, ImTypeArguments typeArguments) + | ImTypeVarRef(ref ImTypeVar typeVariable) + +ImTypeVars * ImTypeVar + +ImTypeVar(String name) + ImFunction(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, - String name, + String name, + ImTypeVars typeVariables, 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, + String name, + ImTypeVars typeVariables, ImVars fields, - ImMethods methods, - java.util.List superClasses) + ImMethods methods, + ImFunctions functions, + java.util.List superClasses) ImMethods * ImMethod -ImMethod(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, - String name, +ImMethod(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, + ref ImClassType methodClass, + String name, ref ImFunction implementation, java.util.List subMethods, boolean isAbstract) @@ -75,6 +95,9 @@ 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 = @@ -87,18 +110,26 @@ ImLExpr = -ImClassRelatedExpr = - ImMethodCall(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImMethod method, ImExpr receiver, ImExprs arguments, boolean tuplesEliminated) - | ImAlloc(ref ImClass clazz) - | ImDealloc(ref ImClass clazz, ImExpr obj) - | ImMemberAccess(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr receiver, ref ImVar var) - | ImInstanceof(ImExpr obj, ref ImClass clazz) - | ImTypeIdOfObj(ImExpr obj, ref ImClass clazz) - | ImTypeIdOfClass(ref ImClass clazz) +ImClassRelatedExpr = + ImMemberOrMethodAccess + | ImClassRelatedExprWithClass + +ImMemberOrMethodAccess = + ImMethodCall(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImMethod method, ImTypeArguments typeArguments, ImExpr receiver, ImExprs arguments, boolean tuplesEliminated) + | ImMemberAccess(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ImExpr receiver, ImTypeArguments typeArguments, ref ImVar var, ImExprs indexes) + +ImClassRelatedExprWithClass = + ImAlloc(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImClassType clazz) + | ImDealloc(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImClassType clazz, ImExpr obj) + | ImInstanceof(ImExpr obj, ref ImClassType clazz) + | ImTypeIdOfObj(ImExpr obj, ref ImClassType clazz) + | ImTypeIdOfClass(ref ImClassType clazz) + + ImCall = - ImFunctionCall(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImFunction func, ImExprs arguments + ImFunctionCall(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImFunction func, ImTypeArguments typeArguments, ImExprs arguments , boolean tuplesEliminated, de.peeeq.wurstscript.translation.imtranslation.CallType callType) | ImOperatorCall(de.peeeq.wurstscript.WurstOperator op, ImExprs arguments) @@ -107,9 +138,13 @@ ImConst = | ImRealVal(String valR) | ImStringVal(String valS) | ImBoolVal(boolean valB) - | ImFuncRef(ref ImFunction func) + | ImFuncRef(@ignoreForEquality de.peeeq.wurstscript.ast.Element trace, ref ImFunction func) | ImNull(ref ImType type) +ImTypeArguments * ImTypeArgument + +ImTypeArgument(ref ImType type, java.util.Map> typeClassBinding) + // helper types: @@ -118,14 +153,15 @@ JassImElementWithName = ImVar | ImFunction | ImClass | ImMethod ElementWithTrace = ImVar | ImFunction | ImClass | ImMethod | ImIf | ImLoop | ImExitwhen | ImReturn | ImSet | ImSetTuple | ImSetArray | ImSetArrayMulti | ImSetArrayTuple | ImMethodCall | ImFunctionCall | ImCompiletimeExpr | ImVarArrayAccess | ImMemberAccess - | ImProg + | ImProg | ImFuncRef + | ImAlloc | ImDealloc ElementWithTypes = ImTupleType | ImTupleArrayType ElementWithVar = ImVarAccess | ImVarArrayAccess | ImVarArrayMultiAccess | ImMemberAccess -ImPrintable = ImStmt | ImFunction | ImProg | ImVar | ImType | ImStmts | ImExprOpt +ImPrintable = ImStmt | ImFunction | ImProg | ImVar | ImType | ImStmts | ImExprOpt | ImType | ImTypeVar | ImClass ImVarWrite = ImSet | ImSetArray | ImSetArrayMulti | ImSetArrayTuple | ImSetTuple ImVarRead = ImVarAccess | ImVarArrayAccess | ImVarArrayMultiAccess @@ -140,7 +176,7 @@ ImPrintable.print(java.lang.Appendable sb, int indent) returns void implemented by de.peeeq.wurstscript.translation.imtranslation.ImPrinter.print -ImPrintable.toString() +Element.toString() returns String implemented by de.peeeq.wurstscript.translation.imtranslation.ImPrinter.asString @@ -315,7 +351,7 @@ ImMethod.attrClass() returns ImClass implemented by de.peeeq.wurstscript.translation.imtojass.ImAttributes.attrClass -ImClass.attrTypeId +ImClass.attrTypeId() returns int implemented by de.peeeq.wurstscript.translation.imtranslation.TypeId.get @@ -338,4 +374,4 @@ ImProg.attrSubclasses ImLExpr.isUsedAsLValue() returns boolean implemented by de.peeeq.wurstscript.translation.imtranslation.LValues.isUsedAsLValue - \ No newline at end of file + diff --git a/de.peeeq.wurstscript/parserspec/wurstscript.parseq b/de.peeeq.wurstscript/parserspec/wurstscript.parseq index 3c77e8daf..16cadfae2 100644 --- a/de.peeeq.wurstscript/parserspec/wurstscript.parseq +++ b/de.peeeq.wurstscript/parserspec/wurstscript.parseq @@ -115,7 +115,9 @@ ArraySizes * Expr TypeParamDefs * TypeParamDef -TypeParamDef(de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId) +TypeParamDef(de.peeeq.wurstscript.parser.WPos source, Modifiers modifiers, Identifier nameId, TypeParamConstraints typeParamConstraints) + +TypeParamConstraints = NoTypeParamConstraints() | TypeExprList WParameters * WParameter @@ -343,7 +345,9 @@ AstElementWithIndexes = ExprMemberArrayVar | ExprVarArrayAccess ClassOrModule = ClassDef | ModuleDef -StructureDef = ClassOrModuleOrModuleInstanciation | InterfaceDef +StructureDef = ClassOrModuleOrModuleInstanciation | ClassOrInterface + +ClassOrInterface = ClassDef | InterfaceDef ClassOrModuleInstanciation = ClassDef | ModuleInstanciation ClassOrModuleOrModuleInstanciation = ClassOrModule | ClassOrModuleInstanciation @@ -518,6 +522,10 @@ Element.attrNearestClassDef() returns @Nullable ClassDef implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassDef +Element.attrNearestClassOrInterface() + returns @Nullable ClassOrInterface + implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassOrInterface + Element.attrNearestClassOrModule() returns @Nullable ClassOrModule implemented by de.peeeq.wurstscript.attributes.AttrNearest.nearestClassOrModule 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 893b43020..5def4e51f 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 @@ -132,6 +132,15 @@ classDef: ENDBLOCK)? ; +typeclassDef: + modifiersWithDoc 'typeclass' name=ID typeParams + ('extends' implemented+=typeExpr (',' implemented+=typeExpr)*)? + NL (STARTBLOCK + classSlots + ENDBLOCK)? + ; + + enumDef: modifiersWithDoc 'enum' name=ID NL (STARTBLOCK (enumMembers+=ID NL)* ENDBLOCK)?; @@ -406,7 +415,10 @@ shortFormalParameter: typeExpr? name=ID; typeParams: ('<' (params+=typeParam (',' params+=typeParam)*)? '>')?; -typeParam: name=ID; +typeParam: name=ID typeParamConstraints?; + +typeParamConstraints: ':' (constraints+=typeExpr ('and' constraints+=typeExpr)*)?; + stmtForLoop: forRangeLoop diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java index daa3f9e85..0a7fc02e7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java @@ -156,7 +156,7 @@ protected void print(String message) { runTests.runTests(compiler.getImProg(), null, null); for (RunTests.TestFailure e : runTests.getFailTests()) { - gui.sendError(new CompileError(e.getFunction().attrTrace().attrErrorPos(), e.getMessage())); + gui.sendError(new CompileError(e.getFunction(), e.getMessage())); if (runArgs.isGui()) { // when using graphical user interface, send stack trace to GUI for (ILStackFrame sf : Utils.iterateReverse(e.getStackTrace().getStackFrames())) { 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 3bb62593f..ae6a3c973 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 @@ -267,7 +267,7 @@ private ImExpr constantToExprHashtable(ImCompiletimeExpr cte, Element trace, Arr WPos errorPos = trace.attrErrorPos(); ImFunction initHashtable = findNative("InitHashtable", errorPos); ImStmts stmts = JassIm.ImStmts( - JassIm.ImSet(trace, JassIm.ImVarAccess(htVar), JassIm.ImFunctionCall(trace, initHashtable, JassIm.ImExprs(), false, CallType.NORMAL)) + JassIm.ImSet(trace, JassIm.ImVarAccess(htVar), JassIm.ImFunctionCall(trace, initHashtable, JassIm.ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL)) ); // we have to collect all values after all compiletime functions have run, so use delayedActions @@ -278,7 +278,7 @@ private ImExpr constantToExprHashtable(ImCompiletimeExpr cte, Element trace, Arr if (v instanceof ILconstInt) { ILconstInt iv = (ILconstInt) v; ImFunction SaveInteger = findNative("SaveInteger", errorPos); - stmts.add(JassIm.ImFunctionCall(trace, SaveInteger, JassIm.ImExprs( + stmts.add(JassIm.ImFunctionCall(trace, SaveInteger, JassIm.ImTypeArguments(), JassIm.ImExprs( JassIm.ImVarAccess(htVar), JassIm.ImIntVal(key.getParentkey()), JassIm.ImIntVal(key.getChildkey()), 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 bba8a7d39..3bac91f37 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 @@ -394,6 +394,11 @@ public JassProg transformProgToJass() { imTranslator2.assertProperties(); checkNoCompiletimeExpr(imProg2); int stage = 2; + // eliminate + beginPhase(2, "Eliminate generics"); + new EliminateGenerics(imTranslator2, imProg2).transform(); + printDebugImProg("./test-output/im " + stage++ + "_genericsEliminated.im"); + // eliminate classes beginPhase(2, "translate classes"); 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 46adeec0e..0298989e0 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 @@ -571,6 +571,11 @@ public List> case_NativeType(NativeType nativeType) return description(nativeType); } + @Override + public List> case_NoTypeParamConstraints(NoTypeParamConstraints noTypeParamConstraints) { + return string("No type parameter constraints given."); + } + @Override public List> case_StmtForRangeUp(StmtForRangeUp stmtForRangeUp) { return string("Execute the body several times, counting up"); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/ModuleExpander.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/ModuleExpander.java index a8686804d..01338ee99 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/ModuleExpander.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/ModuleExpander.java @@ -109,7 +109,7 @@ public static ModuleInstanciations expandModules(ModuleInstanciation mi) { return mi.getP_moduleInstanciations(); } - private static T smartCopy(T e, List> typeReplacements) { + public static T smartCopy(T e, List> typeReplacements) { List, TypeExpr>> replacementsByPath = Lists.newArrayList(); calcReplacementsByPath(typeReplacements, replacementsByPath, e, ImmutableList.emptyList()); @@ -118,7 +118,7 @@ private static T smartCopy(T e, List, TypeExpr> rep : replacementsByPath) { - doReplacement(copy, rep.getA(), (TypeExpr) rep.getB().copy()); + doReplacement(copy, rep.getA(), rep.getB().copy()); } @SuppressWarnings("unchecked") 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 227dcdb50..517585082 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 @@ -67,6 +67,17 @@ public class AttrNearest { return node.getParent().attrNearestClassOrModule(); } + public static ClassOrInterface nearestClassOrInterface(Element node) { + while (node != null) { + if (node instanceof ClassOrInterface) { + return ((ClassOrInterface) node); + } + node = node.getParent(); + } + return null; + } + + public static @Nullable NamedScope nearestNamedScope(@Nullable Element node) { if (node == null) { 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 d7bb92ba8..73c95c7d7 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 @@ -66,7 +66,7 @@ private static WPos getParentSource(Element e) { } parent = parent.getParent(); } - return new WPos("", new LineOffsets(), 0, 0); + return new WPos("", new LineOffsets(), 0, -1); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeConstraints.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeConstraints.java new file mode 100644 index 000000000..477ca6051 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrTypeConstraints.java @@ -0,0 +1,7 @@ +package de.peeeq.wurstscript.attributes; + +/** + * + */ +public class AttrTypeConstraints { +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java index 778447aee..6bb7b150c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java @@ -1,5 +1,7 @@ package de.peeeq.wurstscript.attributes; +import de.peeeq.wurstscript.jassIm.Element; +import de.peeeq.wurstscript.jassIm.ImTypeVarRef; import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.utils.LineOffsets; import org.eclipse.jdt.annotation.Nullable; @@ -41,6 +43,15 @@ public CompileError(@Nullable WPos source, String message, ErrorType errorType) } + public CompileError(Element e, String msg) { + this(e.attrTrace().attrErrorPos(), msg); + } + + public CompileError(de.peeeq.wurstscript.ast.Element e, String msg) { + this(e.attrErrorPos(), msg); + } + + public WPos getSource() { return source; } 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 58aa173c1..65d207630 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 @@ -417,4 +417,8 @@ public static String description(NoSuperConstructorCall noSuperConstructorCall) public static String description(SomeSuperConstructorCall s) { return "Calling the super constructor"; } + + public static String description(NoTypeParamConstraints noTypeParamConstraints) { + return "no type parameter constraints"; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/GenericsHelper.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/GenericsHelper.java index 78446b15a..1785d071f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/GenericsHelper.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/GenericsHelper.java @@ -1,11 +1,16 @@ package de.peeeq.wurstscript.attributes; +import com.google.common.collect.ImmutableMap; import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.types.VariableBinding; import de.peeeq.wurstscript.types.WurstTypeBoundTypeParam; +import org.eclipse.jdt.annotation.Nullable; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 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 5f69ef4ba..d30b9ba82 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 @@ -6,10 +6,8 @@ import de.peeeq.wurstscript.parser.WPos; import de.peeeq.wurstscript.types.VariableBinding; import de.peeeq.wurstscript.types.WurstType; -import de.peeeq.wurstscript.types.WurstTypeBoundTypeParam; import de.peeeq.wurstscript.types.WurstTypeVararg; import de.peeeq.wurstscript.utils.Utils; -import fj.data.TreeMap; import org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -22,14 +20,16 @@ public class FuncLink extends DefLink { private final WurstType returnType; private final List parameterNames; private final List parameterTypes; + private final VariableBinding mapping; public FuncLink(Visibility visibility, WScope definedIn, List typeParams, - @Nullable WurstType receiverType, FunctionDefinition def, List parameterNames, List parameterTypes, WurstType returnType) { + @Nullable WurstType receiverType, FunctionDefinition def, List parameterNames, List parameterTypes, WurstType returnType, VariableBinding mapping) { super(visibility, definedIn, typeParams, receiverType); this.def = def; this.returnType = returnType; this.parameterTypes = parameterTypes; this.parameterNames = parameterNames; + this.mapping = mapping; } public static FuncLink create(FunctionDefinition func, WScope definedIn) { @@ -43,7 +43,7 @@ public static FuncLink create(FunctionDefinition func, WScope definedIn) { .collect(Collectors.toList()); WurstType lreturnType = func.attrReturnTyp(); WurstType lreceiverType = calcReceiverType(definedIn, func); - return new FuncLink(visibiliy, definedIn, typeParams, lreceiverType, func, lParameterNames, lParameterTypes, lreturnType); + return new FuncLink(visibiliy, definedIn, typeParams, lreceiverType, func, lParameterNames, lParameterTypes, lreturnType, VariableBinding.emptyMapping()); } @@ -73,7 +73,7 @@ public FuncLink withVisibility(Visibility newVis) { if (newVis == getVisibility()) { return this; } - return new FuncLink(newVis, getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType); + return new FuncLink(newVis, getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); } @@ -153,7 +153,7 @@ public FuncLink withTypeArgBinding(Element context, VariableBinding binding) { List newTypeParams = getTypeParams().stream() .filter(tp -> !binding.contains(tp)) .collect(Collectors.toList()); - return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, newReceiverType, def, parameterNames, newParamTypes, newReturnType); + return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, newReceiverType, def, parameterNames, newParamTypes, newReturnType, binding); } else { return this; } @@ -165,7 +165,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); + return new FuncLink(getVisibility(), getDefinedIn(), newTypeParams, getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); } @Override @@ -175,7 +175,7 @@ public WurstType getTyp() { @Override public FuncLink withDef(NameDef def) { - return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), (FunctionDefinition) def, getParameterNames(), getParameterTypes(), getReturnType()); + return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), (FunctionDefinition) def, getParameterNames(), getParameterTypes(), getReturnType(), mapping); } private WurstType adjustType(Element context, WurstType t, VariableBinding binding) { @@ -219,7 +219,7 @@ public boolean isVarargMethod() { public FuncLink withConfigDef() { FunctionDefinition def = (FunctionDefinition) this.def.attrConfigActualNameDef(); - return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType); + return new FuncLink(getVisibility(), getDefinedIn(), getTypeParams(), getReceiverType(), def, parameterNames, parameterTypes, returnType, mapping); } public FuncLink hidingPrivate() { @@ -277,6 +277,10 @@ public int hashCode() { return Objects.hash(def); } + public VariableBinding getVariableBinding() { + return mapping; + } + public boolean hasIfNotDefinedAnnotation() { return def.attrHasAnnotation("ifNotDefined"); } 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 7f7cd3087..eb937b695 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 @@ -747,4 +747,8 @@ public static void prettyPrint(SomeSuperConstructorCall c, Spacer spacer, String sb.append(")\n"); } + + public static void prettyPrint(NoTypeParamConstraints noTypeParamConstraints, Spacer spacer, StringBuilder sb, int indent) { + // nothing + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/ILconstUnsafeDefault.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/ILconstUnsafeDefault.java new file mode 100644 index 000000000..ca1a28f39 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/ILconstUnsafeDefault.java @@ -0,0 +1,30 @@ +package de.peeeq.wurstscript.intermediatelang; + +import de.peeeq.wurstscript.jassIm.ImTypeVar; +import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.types.WurstTypeInfer; + +public class ILconstUnsafeDefault extends ILconstAbstract { + + + private final ImTypeVar typeVariable; + + public ILconstUnsafeDefault(ImTypeVar typeVariable) { + this.typeVariable = typeVariable; + } + + @Override + public String print() { + return "unsafe-default<" + typeVariable.getName() + ">"; + } + + public WurstType getType() { + return WurstTypeInfer.instance(); + } + + @Override + public boolean isEqualTo(ILconst other) { + return other instanceof ILconstUnsafeDefault; + } + +} 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 35904afcb..e6eeb6e9d 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 @@ -9,6 +9,7 @@ import de.peeeq.wurstscript.ast.WPackage; import de.peeeq.wurstscript.intermediatelang.*; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtranslation.EliminateGenerics; import de.peeeq.wurstscript.translation.imtranslation.ImPrinter; import org.eclipse.jdt.annotation.Nullable; @@ -17,6 +18,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; +import java.util.stream.Stream; public class EvaluateExpr { @@ -91,11 +93,16 @@ public static ILconst eval(ImTupleExpr e, ProgramState globalState, LocalState l } public static ILconst eval(ImTupleSelection e, ProgramState globalState, LocalState localState) { - ILconstTuple t = (ILconstTuple) e.getTupleExpr().evaluate(globalState, localState); - if (e.getTupleIndex() >= t.values().size()) { - throw new InterpreterException(globalState, "Trying to get element " + e.getTupleIndex() + " of tuple value " + t); + ILconst tupleE = e.getTupleExpr().evaluate(globalState, localState); + if (tupleE instanceof ILconstTuple) { + ILconstTuple t = (ILconstTuple) tupleE; + if (e.getTupleIndex() >= t.values().size()) { + throw new InterpreterException(globalState, "Trying to get element " + e.getTupleIndex() + " of tuple value " + t); + } + return t.getValue(e.getTupleIndex()); + } else { + throw new InterpreterException(globalState, "Tuple " + e + " evaluated to " + tupleE); } - return t.getValue(e.getTupleIndex()); } @@ -194,30 +201,35 @@ public static ILconst eval(ImMemberAccess ma, ProgramState globalState, LocalSta if (receiver.getVal() == 0) { throw new RuntimeException("Null pointer dereference"); } - return notNull(globalState.getArrayVal(ma.getVar(), Collections.singletonList(receiver.getVal())), ma.getVar().getType(), "Variable " + ma.getVar().getName() + " is null.", false); + ILconst res = notNull(globalState.getArrayVal(ma.getVar(), Collections.singletonList(receiver.getVal())), ma.getVar().getType(), "Variable " + ma.getVar().getName() + " is null.", false); + for (ImExpr index : ma.getIndexes()) { + ILconstInt indexE = (ILconstInt) index.evaluate(globalState, localState); + res = ((ILconstArray) res).get(indexE.getVal()); + } + return res; } public static ILconst eval(ImAlloc imAlloc, ProgramState globalState, LocalState localState) { - return new ILconstInt(globalState.allocate(imAlloc.getClazz(), imAlloc.attrTrace())); + return new ILconstInt(globalState.allocate(imAlloc.getClazz().getClassDef(), imAlloc.attrTrace())); } public static ILconst eval(ImDealloc imDealloc, ProgramState globalState, LocalState localState) { ILconstInt obj = (ILconstInt) imDealloc.getObj().evaluate(globalState, localState); - globalState.deallocate(obj.getVal(), imDealloc.getClazz(), imDealloc.attrTrace()); + globalState.deallocate(obj.getVal(), imDealloc.getClazz().getClassDef(), imDealloc.attrTrace()); return ILconstNull.instance(); } public static ILconst eval(ImInstanceof e, ProgramState globalState, LocalState localState) { ILconstInt obj = (ILconstInt) e.getObj().evaluate(globalState, localState); - return ILconstBool.instance(globalState.isInstanceOf(obj.getVal(), e.getClazz(), e.attrTrace())); + return ILconstBool.instance(globalState.isInstanceOf(obj.getVal(), e.getClazz().getClassDef(), e.attrTrace())); } public static ILconst eval(ImTypeIdOfClass e, ProgramState globalState, LocalState localState) { - return new ILconstInt(e.getClazz().attrTypeId()); + return new ILconstInt(e.getClazz().getClassDef().attrTypeId()); } public static ILconst eval(ImTypeIdOfObj e, @@ -322,9 +334,29 @@ public ILconst get() { } } - public static ILaddress evaluateLvalue(ImMemberAccess e, ProgramState globalState, LocalState localState) { - ILconst r = e.getReceiver().evaluate(globalState, localState); - throw new InterpreterException(e.attrTrace(), "Cannot evaluate " + r); + public static ILaddress evaluateLvalue(ImMemberAccess va, ProgramState globalState, LocalState localState) { + ImVar v = va.getVar(); + int receiver = ((ILconstInt) va.getReceiver().evaluate(globalState, localState)).getVal(); + State state; + state = globalState; + List indexes = + Stream.concat( + Stream.of(receiver), + va.getIndexes().stream() + .map(ie -> ((ILconstInt) ie.evaluate(globalState, localState)).getVal()) + ) + .collect(Collectors.toList()); + return new ILaddress() { + @Override + public void set(ILconst value) { + state.setArrayVal(v, indexes, value); + } + + @Override + public ILconst get() { + return state.getArrayVal(v, indexes); + } + }; } @@ -355,4 +387,12 @@ 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) { + return imCast.getExpr().evaluate(globalState, localState); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java index e8be1af13..5a4f04768 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java @@ -44,7 +44,7 @@ public String getMessage() { sb.append("... when executing compiletime expression "); } - if (trace != null) { + if (trace != null && !trace.isArtificial()) { String file = new File(trace.getFile()).getName(); sb.append(" in ").append(file).append(":").append(trace.getLine()); } 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 91d2371ea..08be354e6 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 @@ -193,8 +193,8 @@ public StackTrace(Deque stackFrames) { } public void appendTo(StringBuilder sb) { - for (int i = stackFrames.size() - 1; i >= 0; i--) { - sb.append(stackFrames.get(i).getMessage()); + for (ILStackFrame stackFrame : stackFrames) { + sb.append(stackFrame.getMessage()); sb.append("\n"); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ControlFlowGraph.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ControlFlowGraph.java index e9a528b03..1a00a02a7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ControlFlowGraph.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/ControlFlowGraph.java @@ -123,7 +123,7 @@ private List getSuccessorList(ImStmt s, int i) { } e = e.getParent(); if (e == null) { - throw new CompileError(s.attrTrace().attrErrorPos(), "exitwhen outside of loop"); + throw new CompileError(s, "exitwhen outside of loop"); } } } 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 1d6fee4c9..b8c341a10 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 @@ -96,6 +96,11 @@ 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); @@ -116,6 +121,11 @@ public Boolean case_ImAlloc(ImAlloc imAlloc) { return true; } + @Override + public Boolean case_ImCast(ImCast imCast) { + return quickcheckHasSideeffects(imCast.getExpr()); + } + @Override public Boolean case_ImCompiletimeExpr(ImCompiletimeExpr imCompiletimeExpr) { return true; 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 2ad11eba7..eb279bd7d 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 @@ -1254,7 +1254,7 @@ private TypeParamDefs transformTypeParams(@Nullable TypeParamsContext typeParams private TypeParamDef transformTypeParam(TypeParamContext p) { Modifiers modifiers = Ast.Modifiers(); - return Ast.TypeParamDef(source(p), modifiers, text(p.name)); + return Ast.TypeParamDef(source(p), modifiers, text(p.name), Ast.NoTypeParamConstraints()); } private WImport transformImport(WImportContext i) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/lua/translation/ExprTranslation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/lua/translation/ExprTranslation.java index 72bbf4b12..80637de63 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/lua/translation/ExprTranslation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/lua/translation/ExprTranslation.java @@ -10,12 +10,12 @@ public class ExprTranslation { public static LuaExpr translate(ImAlloc e, LuaTranslator tr) { LuaTableFields fields = LuaAst.LuaTableFields(); - ImClass clazz = e.getClazz(); + ImClass clazz = e.getClazz().getClassDef(); fields.add(LuaAst.LuaTableNamedField("wurst_typeId", LuaAst.LuaExprIntVal("" + clazz.attrTypeId()))); - for (ImMethod m : clazz.getMethods()) { - LuaFunction luaMethod = tr.luaMethod.getFor(m); - fields.add(LuaAst.LuaTableNamedField(luaMethod.getName(), LuaAst.LuaExprFuncRef(tr.luaFunc.getFor(m.getImplementation())))); - } +// for (ImMethod m : clazz.getMethods()) { +// LuaFunction luaMethod = tr.luaMethod.getFor(m); +// fields.add(LuaAst.LuaTableNamedField(luaMethod.getName(), LuaAst.LuaExprFuncRef(tr.luaFunc.getFor(m.getImplementation())))); +// } return LuaAst.LuaTableConstructor(fields); // TODO any fields required? typeid? } @@ -143,7 +143,7 @@ public static LuaExpr translate(ImTupleSelection e, LuaTranslator tr) { } public static LuaExpr translate(ImTypeIdOfClass e, LuaTranslator tr) { - int i = tr.typeId.getFor(e.getClazz()); + int i = tr.typeId.getFor(e.getClazz().getClassDef()); return LuaAst.LuaExprIntVal("" + i); } @@ -171,4 +171,11 @@ 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) { + return imCast.getExpr().translateToLua(tr); + } } 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 f3f80d53d..d5dc8531c 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 @@ -1291,7 +1291,19 @@ private TypeParamDefs transformTypeParams(TypeParamsContext typeParams) { private TypeParamDef transformTypeParam(TypeParamContext p) { Modifiers modifiers = Ast.Modifiers(); - return Ast.TypeParamDef(source(p), modifiers, text(p.name)); + TypeParamConstraints typeParamClasses = tranformTypeParamConstraints(p.typeParamConstraints()); + return Ast.TypeParamDef(source(p), modifiers, text(p.name), typeParamClasses); + } + + private TypeParamConstraints tranformTypeParamConstraints(TypeParamConstraintsContext tc) { + if (tc == null) { + return Ast.NoTypeParamConstraints(); + } + TypeExprList res = Ast.TypeExprList(); + for (TypeExprContext t : tc.constraints) { + res.add(transformTypeExpr(t)); + } + return res; } private WImport transformImport(WImportContext i) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/ExtendedWurstLexer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/ExtendedWurstLexer.java index 413361092..f67876945 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/ExtendedWurstLexer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/parser/antlr/ExtendedWurstLexer.java @@ -323,10 +323,10 @@ private void handleIndent(int n, Token token, int start, int stop) { spacesPerIndent = n; } else if (tabWarning == null && n != indentationLevels.peek() + spacesPerIndent) { String message = "Inconsistent indentation: Earlier in this file " + spacesPerIndent + " spaces were used for indentation and here it is " + (n - indentationLevels.peek()) + " spaces."; - tabWarning = new CompileError(new WPos("", lineOffsets, token.getStartIndex(), token.getStopIndex()), message); + tabWarning = new CompileError(new WPos("", lineOffsets, lineOffsets.get(token.getLine()), token.getStopIndex()), message); } if (tabWarning == null && n % 2 == 1) { - tabWarning = new CompileError(new WPos("", lineOffsets, token.getStartIndex(), token.getStopIndex()), "Use an even number of spaces for indentation."); + tabWarning = new CompileError(new WPos("", lineOffsets, lineOffsets.get(token.getLine()), token.getStopIndex()), "Use an even number of spaces for indentation."); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/DefaultValue.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/DefaultValue.java index 6e12689fd..9e01a5697 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/DefaultValue.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/DefaultValue.java @@ -47,4 +47,11 @@ private static Supplier makeSupplier(int depth, ImType entryType) { return () -> new ILconstArray(makeSupplier(depth - 1, entryType)); } + public static ILconst get(ImTypeVarRef e) { + return new ILconstUnsafeDefault(e.getTypeVariable()); + } + + public static ILconst get(ImClassType ct) { + return new ILconstInt(0); + } } 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 b8545fa13..78aa84551 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 @@ -119,7 +119,7 @@ public static JassExprVarArrayAccess translate(ImVarArrayAccess e, ImToJassTrans public static JassExpr translate(ImClassRelatedExpr e, ImToJassTranslator translator) { - throw new RuntimeException("Eliminate method calls before translating to jass"); + throw new RuntimeException("Eliminate method calls before translating to jass:\n" + e); } @@ -134,4 +134,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); + } } 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 b62d7443f..00a1b2328 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 @@ -1,10 +1,12 @@ package de.peeeq.wurstscript.translation.imtojass; import com.google.common.collect.Lists; +import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.*; import java.util.List; +import java.util.stream.Collectors; public class ImAttrType { @@ -18,6 +20,7 @@ public static ImType getType(ImFuncRef e) { public static ImType getType(ImFunctionCall e) { ImType t = e.getFunc().getReturnType(); + t = substituteType(t, e.getTypeArguments(), e.getFunc().getTypeVariables()); if (e.getTuplesEliminated()) { if (t instanceof ImTupleType) { ImTupleType tt = (ImTupleType) t; @@ -27,6 +30,24 @@ public static ImType getType(ImFunctionCall e) { return t; } + public static ImType substituteType(ImType type, List generics, List typeVars) { + return type.match(new TypeRewriteMatcher() { + + @Override + public ImType case_ImTypeVarRef(ImTypeVarRef t) { + int index = typeVars.indexOf(t.getTypeVariable()); + if (index < 0) { + return t; + } else if (index >= generics.size()) { + throw new RuntimeException("Could not find replacement for " + t + " when replacing " + typeVars + " with " + generics); + } + return generics.get(index).getType(); + } + + }); + } + + public static ImType getType(ImIntVal e) { return WurstTypeInt.instance().imTranslateType(); } @@ -125,7 +146,23 @@ public static ImType getType(ImMethodCall mc) { } public static ImType getType(ImMemberAccess e) { - return e.getVar().getType(); + ImType t = e.getVar().getType(); + ImType receiverType1 = e.getReceiver().attrTyp(); + if (receiverType1 instanceof ImClassType) { + ImClassType receiverType = (ImClassType) receiverType1; + ImTypeArguments typeArgs = e.getTypeArguments(); + try { + if (typeArgs.isEmpty()) { + typeArgs = receiverType.getTypeArguments(); + } + t = substituteType(t, typeArgs, receiverType.getClassDef().getTypeVariables()); + return t; + } catch (Exception ex) { + throw new RuntimeException("Could not determine type of " + e + " with receiverType " + receiverType, ex); + } + } else { + return t; + } } public static ImType getType(ImAlloc imAlloc) { @@ -156,4 +193,12 @@ public static ImType getType(ImGetStackTrace imGetStackTrace) { 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(); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttributes.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttributes.java index b710989dc..8d346153c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttributes.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImAttributes.java @@ -7,6 +7,9 @@ import de.peeeq.wurstscript.translation.imtranslation.FunctionFlagCompiletime; import de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum; +import java.util.ArrayDeque; +import java.util.Deque; + public class ImAttributes { @@ -70,8 +73,20 @@ public static de.peeeq.wurstscript.ast.Element getTrace(ElementWithTrace t) { } public static de.peeeq.wurstscript.ast.Element getTrace(Element t) { - if (t.getParent() != null) { - return t.getParent().attrTrace(); + Deque q = new ArrayDeque<>(); + q.add(t); + while (q.isEmpty()) { + Element e = q.removeFirst(); + if (e == null) { + continue; + } + q.add(e.getParent()); + for (int i = 0; i < e.size(); i++) { + q.add(e.get(i)); + } + if (e instanceof ElementWithTrace) { + return ((ElementWithTrace) e).getTrace(); + } } return Ast.NoExpr(); } @@ -95,11 +110,15 @@ public static ImProg getProg(Element el) { public static ImClass attrClass(ImMethod m) { - if (m.getParent() == null) { - throw new CompileError(m.attrTrace().attrSource(), "Method " + m.getName() + " not attached."); - } - return (ImClass) m.getParent().getParent(); + return m.getMethodClass().getClassDef(); } + public static String translateType(ImTypeVarRef t) { + throw new CompileError(t, "Type variable " + t.getTypeVariable().getName() + " not eliminated."); + } + + public static String translateType(ImClassType imClassType) { + return "integer"; + } } 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 ff09703af..e8c24c87a 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 @@ -19,6 +19,8 @@ import org.eclipse.jdt.annotation.Nullable; import java.util.*; +import java.util.regex.Pattern; + import static de.peeeq.wurstscript.jassAst.JassAst.*; public class ImToJassTranslator { @@ -213,6 +215,7 @@ JassVar getJassVarFor(ImVar v) { } private String jassifyName(String name) { + name = filterInvalidSymbols(name); if (RestrictedCompressedNames.contains(name) || name.startsWith("_")) { name = "w" + name; } @@ -225,6 +228,27 @@ private String jassifyName(String name) { return name; } + private final Pattern jassValidName = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*"); + + /** replaces all invalid characters with underscores*/ + private String filterInvalidSymbols(String name) { + if (jassValidName.matcher(name).matches()) { + return name; + } + StringBuilder sb = new StringBuilder(name.length()); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (!(c >= 'a' && c <= 'z' + || c >= 'A' && c <= 'Z' + || c >= '0' && c <= '9' + || c == '_')) { + c = '_'; + } + sb.append(c); + } + return sb.toString(); + } + private boolean isGlobal(ImVar v) { return globalImVars.contains(v); } 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 f59881ab6..db47e3d4c 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 @@ -42,6 +42,7 @@ public static boolean isEqualType(ImTupleType a, ImType b) { return false; } } + return true; } return false; } @@ -51,4 +52,35 @@ public static boolean isEqualType(ImVoid a, ImType b) { } + public static boolean isEqualType(ImTypeVarRef t, ImType other) { + if (other instanceof ImTypeVarRef) { + ImTypeVarRef o = (ImTypeVarRef) other; + return t.getTypeVariable() == o.getTypeVariable(); + } + return false; + } + + public static boolean isEqualType(ImClassType c, ImType other) { + if (other instanceof ImClassType) { + ImClassType oc = (ImClassType) other; + if (c.getClassDef() != oc.getClassDef()) { + return false; + } + if (c.getTypeArguments().size() != oc.getTypeArguments().size()) { + return false; + } + for (int i = 0; i < c.getTypeArguments().size(); i++) { + ImTypeArgument x = c.getTypeArguments().get(i); + ImTypeArgument y = oc.getTypeArguments().get(i); + if (!x.getType().equalsType(y.getType())) { + return false; + } + if (!x.getTypeClassBinding().equals(y.getTypeClassBinding())) { + return false; + } + } + return true; + } + return false; + } } 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 new file mode 100644 index 000000000..9007d9dd5 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriteMatcher.java @@ -0,0 +1,55 @@ +package de.peeeq.wurstscript.translation.imtojass; + +import com.google.common.collect.ImmutableList; +import de.peeeq.wurstscript.jassIm.*; + +import java.util.stream.Collectors; + +/** + * + */ +public class TypeRewriteMatcher implements ImType.Matcher { + + @Override + public ImType case_ImVoid(ImVoid t) { + return t; + } + + @Override + public ImType case_ImArrayTypeMulti(ImArrayTypeMulti t) { + return JassIm.ImArrayTypeMulti(t.getEntryType().match(this), ImmutableList.copyOf(t.getArraySize())); + } + + @Override + public ImType case_ImTupleType(ImTupleType t) { + return JassIm.ImTupleType(t.getTypes() + .stream() + .map(tt -> tt.match(this)) + .collect(Collectors.toList()), + ImmutableList.copyOf(t.getNames())); + } + + @Override + public ImType case_ImTypeVarRef(ImTypeVarRef t) { + return t; + } + + @Override + public ImType case_ImSimpleType(ImSimpleType t) { + return t; + } + + @Override + public ImType case_ImArrayType(ImArrayType t) { + return JassIm.ImArrayType(t.getEntryType().match(this)); + } + + @Override + public ImType case_ImClassType(ImClassType t) { + ImTypeArguments args = t.getTypeArguments() + .stream() + .map(ta -> JassIm.ImTypeArgument(ta.getType().match(this), ta.getTypeClassBinding())) + .collect(Collectors.toCollection(JassIm::ImTypeArguments)); + return JassIm.ImClassType(t.getClassDef(), args); + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriter.java new file mode 100644 index 000000000..1382afccb --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/TypeRewriter.java @@ -0,0 +1,101 @@ +package de.peeeq.wurstscript.translation.imtojass; + +import com.google.common.collect.ImmutableList; +import de.peeeq.wurstscript.jassIm.*; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class TypeRewriter { + + /** + * Visits all elements where a type can be used and + * applies the given function to rewrite the type. + */ + public static void rewriteTypes(Element e, Function rewriteType) { + e.accept(new Element.DefaultVisitor() { + + private ImType rewriteType(ImType type) { + return rewriteType.apply(type); + } + + + @Override + public void visit(ImVar e) { + super.visit(e); + e.setType(rewriteType(e.getType())); + } + + @Override + public void visit(ImFunction e) { + super.visit(e); + e.setReturnType(rewriteType(e.getReturnType())); + } + + @Override + public void visit(ImNull e) { + super.visit(e); + e.setType(rewriteType(e.getType())); + } + + @Override + public void visit(ImTypeArgument e) { + super.visit(e); + e.setType(rewriteType(e.getType())); + } + + @Override + public void visit(ImClass e) { + super.visit(e); + List newSuperClasses = e.getSuperClasses().stream() + .map(tt -> (ImClassType) rewriteType(tt)) + .collect(Collectors.toList()); + e.setSuperClasses(newSuperClasses); + } + + @Override + public void visit(ImMethod e) { + super.visit(e); + e.setMethodClass((ImClassType) rewriteType(e.getMethodClass())); + } + + @Override + public void visit(ImAlloc e) { + super.visit(e); + e.setClazz((ImClassType) rewriteType(e.getClazz())); + } + + @Override + public void visit(ImDealloc e) { + super.visit(e); + e.setClazz((ImClassType) rewriteType(e.getClazz())); + } + + @Override + public void visit(ImInstanceof e) { + super.visit(e); + e.setClazz((ImClassType) rewriteType(e.getClazz())); + } + + @Override + public void visit(ImTypeIdOfObj e) { + super.visit(e); + e.setClazz((ImClassType) rewriteType(e.getClazz())); + } + + @Override + public void visit(ImTypeIdOfClass e) { + super.visit(e); + e.setClazz((ImClassType) rewriteType(e.getClazz())); + } + + @Override + public void visit(ImCast e) { + super.visit(e); + e.setToType(rewriteType(e.getToType())); + } + }); + } + +} 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 c91f2270a..dc5e6669d 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 @@ -1,5 +1,126 @@ package de.peeeq.wurstscript.translation.imtranslation; -public enum AssertProperty { - FLAT, NOTUPLES +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.types.TypesHelper; + +public interface AssertProperty { + AssertProperty FLAT = new AssertProperty() { + @Override + public void check(Element e) { + if (e instanceof ImStatementExpr) { + throw new Error("contains statementExpr " + e); + } + } + }; + + AssertProperty NOTUPLES = new AssertProperty() { + @Override + public void check(Element e) { + if (e instanceof ImTupleExpr + || e instanceof ImTupleSelection + ) { + throw new Error("contains tuple exprs " + e); + } + if (e instanceof ImVar) { + ImVar v = (ImVar) e; + if (TypesHelper.typeContainsTuples(v.getType())) { + throw new Error("contains tuple var: " + v + " in\n" + v.getParent().getParent()); + } + } + } + }; + + static AssertProperty rooted(Element root) { + return new AssertProperty() { + ImFunction currentFunction; + + @Override + public void check(Element e) { + if (e instanceof ImVar) { + checkType(e, ((ImVar) e).getType()); + } else if (e instanceof ImFunction) { + 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) { + checkRooted(e, ((ImVarArrayAccess) e).getVar()); + } else if (e instanceof ImMethodCall) { + checkRooted(e, ((ImMethodCall) e).getMethod()); + } else if (e instanceof ImMemberAccess) { + checkRooted(e, ((ImMemberAccess) e).getVar()); + } else if (e instanceof ImClassRelatedExprWithClass) { + checkType(e, ((ImClassRelatedExprWithClass) e).getClazz()); + } else if (e instanceof ImFunctionCall) { + checkRooted(e, ((ImFunctionCall) e).getFunc()); + } else if (e instanceof ImFuncRef) { + checkRooted(e, ((ImFuncRef) e).getFunc()); + } else if (e instanceof ImTypeArgument) { + checkType(e, ((ImTypeArgument) e).getType()); + } + } + + private void checkType(Element e, ImType type) { + if (type instanceof ImArrayType) { + checkType(e, ((ImArrayType) type).getEntryType()); + } else if (type instanceof ImArrayTypeMulti) { + checkType(e, ((ImArrayTypeMulti) type).getEntryType()); + } else if (type instanceof ImClassType) { + checkRooted(e, ((ImClassType) type).getClassDef()); + for (ImTypeArgument ta : ((ImClassType) type).getTypeArguments()) { + checkType(e, ta.getType()); + } + } else if (type instanceof ImTypeVarRef) { + checkRooted(e, ((ImTypeVarRef) type).getTypeVariable()); + } + } + + public void checkRooted(Element location, Element el) { + try { + Element e = el; + while (e != null) { + if (e == root) { + return; + } + Element parent = e.getParent(); + if (parent == null) { + break; + } + checkContains(location, parent, e); + if (parent instanceof ImFunction && parent != currentFunction) { + throw new CompileError(location, "Element " + el + " is rooted in function " + parent + " but should be in function " + currentFunction); + } + e = parent; + } + } catch (CompileError e) { + throw new CompileError(location, "Element " + el + " not rooted. In ...\n" + location + "\n\n" + e.getMessage()); + } + throw new CompileError(location, "Element " + el + " not rooted. In ...\n" + location); + } + + private void checkContains(Element location, Element parent, Element e) { + for (int i = 0; i < parent.size(); i++) { + if (parent.get(i) == e) { + return; + } + } + throw new CompileError(location, "Element " + e + " does not appear in parent " + parent.getClass().getSimpleName() + "." + + "\nIn ...\n" + location); + } + }; + } + + void check(Element e); } 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 768f23b14..245f489ca 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 @@ -15,9 +15,12 @@ import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Pair; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.function.Supplier; +import java.util.stream.Collectors; import static de.peeeq.wurstscript.attributes.SmallHelpers.superArgs; import static de.peeeq.wurstscript.jassIm.JassIm.*; @@ -76,6 +79,7 @@ private void translate() { } + private void addSuperClasses() { if (classDef.getExtendedClass() instanceof TypeExpr) { TypeExpr extended = (TypeExpr) classDef.getExtendedClass(); @@ -88,10 +92,7 @@ private void addSuperClasses() { } private void addSuperClass(TypeExpr extended) { - if (extended.attrTypeDef() instanceof StructureDef) { - StructureDef sc = (StructureDef) extended.attrTypeDef(); - imClass.getSuperClasses().add(translator.getClassFor(sc)); - } + imClass.getSuperClasses().add((ImClassType) extended.attrTyp().imTranslateType(translator)); } private void createDestroyMethod(List subClasses) { @@ -114,12 +115,17 @@ private void createDestroyMethod(List subClasses) { // call ondestroy methods ClassDef c = classDef; ImFunction scOnDestroy = translator.getFuncFor(c.getOnDestroy()); - f.getBody().add(ImFunctionCall(trace, - scOnDestroy, - ImExprs(ImVarAccess(thisVar)), false, CallType.NORMAL)); + f.getBody().add(ImFunctionCall(trace, scOnDestroy, ImTypeArguments(), ImExprs(ImVarAccess(thisVar)), false, CallType.NORMAL)); // deallocate - f.getBody().add(JassIm.ImDealloc(imClass, JassIm.ImVarAccess(thisVar))); + f.getBody().add(JassIm.ImDealloc(c.getOnDestroy(), imClassType(), JassIm.ImVarAccess(thisVar))); + } + + private ImClassType imClassType() { + ImTypeArguments typeArgs = imClass.getTypeVariables().stream() + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .collect(Collectors.toCollection(JassIm::ImTypeArguments)); + return JassIm.ImClassType(imClass, typeArgs); } /** @@ -157,12 +163,17 @@ private void addOnDestroyActions(ImFunction f, List addTo, ClassOrModule if (c instanceof ClassDef) { ClassDef cd = (ClassDef) c; WurstTypeClass ct = cd.attrTypC(); - if (ct.extendedClass() != null) { + WurstTypeClass extended = ct.extendedClass(); + if (extended != null) { // call onDestroy of super class - ImFunction onDestroy = translator.getFuncFor(ct.extendedClass().getClassDef().getOnDestroy()); - addTo.add(ImFunctionCall(c, - onDestroy, - ImExprs(ImVarAccess(thisVar)), false, CallType.NORMAL)); + ImFunction onDestroy = translator.getFuncFor(extended.getClassDef().getOnDestroy()); + ImTypeArguments typeArgs = ImTypeArguments(); + for (WurstTypeBoundTypeParam tp : extended.getTypeParameters()) { + if (tp.isTemplateTypeParameter()) { + typeArgs.add(tp.imTranslateToTypeArgument(translator)); + } + } + addTo.add(ImFunctionCall(c, onDestroy, typeArgs, ImExprs(ImVarAccess(thisVar)), false, CallType.NORMAL)); } } } @@ -223,14 +234,13 @@ private void translateVars(ClassOrModuleInstanciation c) { private void translateVar(GlobalVarDef s) { ImVar v = translator.getVarFor(s); if (s.attrIsDynamicClassMember()) { - // for dynamic class members create an array - ImType t = s.attrTyp().imTranslateType(); - v.setType(ImHelper.toArray(t)); + // add dynamic class members to the class + imClass.getFields().add(v); dynamicInits.add(Pair.create(v, s.getInitialExpr())); } else { // static class member translator.addGlobalInitalizer(v, classDef.attrNearestPackage(), s.getInitialExpr()); + translator.addGlobal(v); } - translator.addGlobal(v); } private void translateMethods(ClassOrModuleInstanciation c, List subClasses) { @@ -320,18 +330,18 @@ private void createNewFunc(ConstructorDef constr) { Map varReplacements = Maps.newLinkedHashMap(); for (WParameter p : constr.getParameters()) { - ImVar imP = ImVar(p, p.attrTyp().imTranslateType(), p.getName(), false); + ImVar imP = ImVar(p, p.attrTyp().imTranslateType(translator), p.getName(), false); varReplacements.put(translator.getVarFor(p), imP); f.getParameters().add(imP); } - ImVar thisVar = JassIm.ImVar(constr, TypesHelper.imInt(), "this", false); + ImVar thisVar = JassIm.ImVar(constr, imClassType(), "this", false); varReplacements.put(translator.getThisVar(constr), thisVar); f.getLocals().add(thisVar); // allocate class - f.getBody().add(ImSet(trace, ImVarAccess(thisVar), JassIm.ImAlloc(imClass))); + f.getBody().add(ImSet(trace, ImVarAccess(thisVar), JassIm.ImAlloc(constr, imClassType()))); // call user defined constructor code: ImFunction constrFunc = translator.getConstructFunc(constr); @@ -339,7 +349,11 @@ private void createNewFunc(ConstructorDef constr) { for (ImVar a : f.getParameters()) { arguments.add(ImVarAccess(a)); } - f.getBody().add(ImFunctionCall(trace, constrFunc, arguments, false, CallType.NORMAL)); + ImTypeArguments typeArgs = ImTypeArguments(); + for (ImTypeVar tv : imClass.getTypeVariables()) { + typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())); + } + f.getBody().add(ImFunctionCall(trace, constrFunc, typeArgs, arguments, false, CallType.NORMAL)); // return this @@ -360,20 +374,33 @@ private void createConstructFunc(ConstructorDef constr) { for (Expr a : superArgs(constr)) { arguments.add(a.imTranslateExpr(translator, f)); } - f.getBody().add(ImFunctionCall(trace, superConstrFunc, arguments, false, CallType.NORMAL)); + ImTypeArguments typeArgs = ImTypeArguments(); + ClassDef classDef = constr.attrNearestClassDef(); + assert classDef != null; + WurstType extendedType = classDef.getExtendedClass().attrTyp(); + if (extendedType instanceof WurstTypeClass) { + WurstTypeClass extendedTypeC = (WurstTypeClass) extendedType; + for (WurstTypeBoundTypeParam bt : extendedTypeC.getTypeParameters()) { + if (bt.isTemplateTypeParameter()) { + typeArgs.add(bt.imTranslateToTypeArgument(translator)); + } + } + } + f.getBody().add(ImFunctionCall(trace, superConstrFunc, typeArgs, arguments, false, CallType.NORMAL)); } // initialize vars for (Pair i : translator.getDynamicInits(classDef)) { ImVar v = i.getA(); if (i.getB() instanceof Expr) { Expr e = (Expr) i.getB(); - ImStmt s = ImSet(trace, ImVarArrayAccess(trace, v, ImExprs((ImExpr) ImVarAccess(thisVar))), e.imTranslateExpr(translator, f)); + ImStmt s = ImSet(trace, ImMemberAccess(trace, ImVarAccess(thisVar), ImTypeArguments(), v, ImExprs()), e.imTranslateExpr(translator, f)); + f.getBody().add(s); } else if (i.getB() instanceof ArrayInitializer) { ArrayInitializer ai = (ArrayInitializer) i.getB(); int index = 0; for (Expr e : ai.getValues()) { - ImStmt s = ImSet(trace, ImVarArrayAccess(trace, v, ImExprs(ImVarAccess(thisVar), JassIm.ImIntVal(index))), e.imTranslateExpr(translator, f)); + ImStmt s = ImSet(trace, ImMemberAccess(trace, ImVarAccess(thisVar), ImTypeArguments(), v, ImExprs(JassIm.ImIntVal(index))), e.imTranslateExpr(translator, f)); f.getBody().add(s); index++; } @@ -391,7 +418,7 @@ private void addModuleInits(ImFunction f, ModuleInstanciation mi, ImVar thisVar) // call constructors of used modules: for (ConstructorDef c : mi.getConstructors()) { ImFunction moduleConstr = translator.getConstructFunc(c); - f.getBody().add(JassIm.ImFunctionCall(c, moduleConstr, JassIm.ImExprs(JassIm.ImVarAccess(thisVar)), false, CallType.NORMAL)); + f.getBody().add(ImFunctionCall(c, moduleConstr, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(thisVar)), false, CallType.NORMAL)); } } 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 345500473..4c187dc8a 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 @@ -2,26 +2,35 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import de.peeeq.wurstscript.ast.ClassDef; -import de.peeeq.wurstscript.ast.ConstructorDef; -import de.peeeq.wurstscript.ast.ExprClosure; -import de.peeeq.wurstscript.ast.FuncDef; +import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher; +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.Map.Entry; +import java.util.stream.Collectors; + +import static java.util.Collections.singletonList; public class ClosureTranslator { private final ExprClosure e; private final ImTranslator tr; private final ImFunction f; + // local variables captured in the closure -> class variables private final Map closureVars = Maps.newLinkedHashMap(); + // type arguments captured in the closure -> new type variables + private Map typeVars; private ImFunction impl; + private ImClass c; public ClosureTranslator(ExprClosure e, ImTranslator tr, ImFunction f) { super(); @@ -36,22 +45,31 @@ public ImExpr translate() { return translateAnonFunc(); } else { ImClass c = createClass(); - ImVar clVar = JassIm.ImVar(e, WurstTypeInt.instance().imTranslateType(), "clVar", false); + ImClassType ct = JassIm.ImClassType(c, getClassTypeArguments()); + ImVar clVar = JassIm.ImVar(e, ct, "clVar", false); f.getLocals().add(clVar); ImStmts stmts = JassIm.ImStmts(); // allocate closure - stmts.add(JassIm.ImSet(e, JassIm.ImVarAccess(clVar), JassIm.ImAlloc(c))); + stmts.add(JassIm.ImSet(e, JassIm.ImVarAccess(clVar), JassIm.ImAlloc(e, ct))); callSuperConstructor(clVar, stmts, c); // set closure vars for (Entry entry : closureVars.entrySet()) { ImVar orig = entry.getKey(); ImVar v = entry.getValue(); - stmts.add(JassIm.ImSet(e, JassIm.ImVarArrayAccess(e, v, JassIm.ImExprs((ImExpr) JassIm.ImVarAccess(clVar))), JassIm.ImVarAccess(orig))); + stmts.add(JassIm.ImSet(e, JassIm.ImMemberAccess(e, JassIm.ImVarAccess(clVar), JassIm.ImTypeArguments(), v, JassIm.ImExprs()), JassIm.ImVarAccess(orig))); } return JassIm.ImStatementExpr(stmts, JassIm.ImVarAccess(clVar)); } } + private ImTypeArguments getClassTypeArguments() { + ImTypeArguments res = JassIm.ImTypeArguments(); + for (ImTypeVar typeVar : typeVars.keySet()) { + res.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(typeVar), Collections.emptyMap())); + } + return res; + } + private void callSuperConstructor(ImVar clVar, ImStmts stmts, ImClass c) { WurstType t = e.attrExpectedTyp(); @@ -73,7 +91,7 @@ private void callSuperConstructor(ImVar clVar, ImStmts stmts, ImClass c) { private void callSuperConstructor(ImVar clVar, ImStmts stmts, ImClass c, ConstructorDef constr) { ImFunction cn = tr.getConstructFunc(constr); ImExprs arguments = JassIm.ImExprs(JassIm.ImVarAccess(clVar)); - stmts.add(JassIm.ImFunctionCall(e, cn, arguments, false, CallType.NORMAL)); + stmts.add(JassIm.ImFunctionCall(e, cn, JassIm.ImTypeArguments(), arguments, false, CallType.NORMAL)); } @@ -86,12 +104,12 @@ private ImExpr translateAnonFunc() { if (e.getImplementation().attrTyp() instanceof WurstTypeBool) { impl.getBody().add(JassIm.ImReturn(e, translated)); - impl.setReturnType(WurstTypeBool.instance().imTranslateType()); + impl.setReturnType(WurstTypeBool.instance().imTranslateType(tr)); } else { impl.getBody().add(translated); - impl.setReturnType(WurstTypeVoid.instance().imTranslateType()); + impl.setReturnType(WurstTypeVoid.instance().imTranslateType(tr)); } - return JassIm.ImFuncRef(impl); + return JassIm.ImFuncRef(e, impl); } @@ -120,32 +138,22 @@ public void visit(ImSet s) { private ImClass createClass() { - ImClass superClass = getSuperClass(); + ImClassType superClass = getSuperClass(); FuncDef superMethod = getSuperMethod(); - ImVars fields = JassIm.ImVars(); - ImMethods methods = JassIm.ImMethods(); - List superClasses = java.util.Collections.singletonList(superClass); - ImClass c = JassIm.ImClass(e, "Closure", fields, methods, superClasses); + c = tr.getClassForClosure(e); + c.setName(makeClassName(superClass)); + c.setSuperClasses(singletonList(superClass)); + //JassIm.ImClass(e, "Closure", JassIm.ImTypeVars(), fields, methods, functions, superClasses); tr.imProg().getClasses().add(c); -// ImVars parameters = JassIm.ImVars(); -// parameters.add(tr.getThisVar(e)); -// for (WParameter p : e.getParameters()) { -// parameters.add(tr.getVarFor(p)); -// } -// ImType returnType = e.getImplementation().attrTyp().imTranslateType(); -// if (returnType == null) { -// WLogger.info(e.attrTyp()); -// returnType = JassIm.ImVoid(); -// } -// ImVars locals = JassIm.ImVars(); -// ImStmts body = JassIm.ImStmts(); -// List flags = Collections.emptyList(); -// ImFunction impl JassIm.ImFunction(e, superMethod.getName(), parameters, returnType, locals, body, flags); impl = tr.getFuncFor(e); - ImMethod m = JassIm.ImMethod(e, superMethod.getName(), impl, JassIm.ImMethods(), false); + impl.setName(makeFuncName(superMethod)); + 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); c.getMethods().add(m); OverrideUtils.addOverrideClosure(tr, superMethod, m, e); @@ -160,10 +168,80 @@ private ImClass createClass() { impl.getBody().add(JassIm.ImReturn(e, translated)); } transformTranslated(translated); + + typeVars = rewriteTypeVars(c); return c; } + private String makeClassName(ImClassType superClass) { + String res = + superClass.getClassDef().getName() + + "_line" + e.attrSource().getLine(); + return addScopeNames(res); + } + + private String makeFuncName(FuncDef superClass) { + String res = superClass.getName() + "_line" + e.attrSource().getLine(); + return addScopeNames(res); + } + + private String addScopeNames(String res) { + Element elem = e; + while (elem != null) { + if (elem instanceof NamedScope) { + res = ((NamedScope) elem).getName() + "_" + res; + } + elem = elem.getParent(); + } + return res; + } + + /** + * Replaces all captured type variables with new type variables. + * And adds these type variables to the closure-class c. + */ + private Map rewriteTypeVars(ImClass c) { + Map result = new LinkedHashMap<>(); + ImClassType thisType = JassIm.ImClassType(c, JassIm.ImTypeArguments()); + TypeRewriter.rewriteTypes(c, t -> rewriteType(thisType, result, t)); + return result; + } + + private ImType rewriteType(ImClassType thisType, Map result, ImType type) { + return type.match(new TypeRewriteMatcher() { + @Override + public ImType case_ImClassType(ImClassType t) { + if (t.getClassDef() == c) { + return thisType; + } + return super.case_ImClassType(t); + } + + + @Override + public ImType case_ImTypeVarRef(ImTypeVarRef t) { + ImTypeVar oldTypevar = t.getTypeVariable(); + ImTypeVar newTypevar = result.get(oldTypevar); + if (newTypevar == null) { + newTypevar = JassIm.ImTypeVar(oldTypevar.getName() + "_captured"); + result.put(oldTypevar, newTypevar); + c.getTypeVariables().add(newTypevar); + thisType.getTypeArguments().add( + JassIm.ImTypeArgument( + JassIm.ImTypeVarRef(newTypevar), + Collections.emptyMap())); + } + return JassIm.ImTypeVarRef(newTypevar); + } + }); + } + + + /** + * all uses of local variables are changed to use the + * class variables instead + */ private void transformTranslated(ImExpr t) { final List vas = Lists.newArrayList(); t.accept(new ImExpr.DefaultVisitor() { @@ -180,11 +258,11 @@ public void visit(ImVarAccess va) { for (ImVarAccess va : vas) { ImVar v = getClosureVarFor(va.getVar()); - va.replaceBy(JassIm.ImVarArrayAccess(e, v, JassIm.ImExprs(closureThis()))); + va.replaceBy(JassIm.ImMemberAccess(e, closureThis(), JassIm.ImTypeArguments(), v, JassIm.ImExprs())); } } - private ImExpr closureThis() { + private ImVarAccess closureThis() { return JassIm.ImVarAccess(tr.getThisVar(e)); } @@ -192,8 +270,8 @@ private ImExpr closureThis() { private ImVar getClosureVarFor(ImVar var) { ImVar v = closureVars.get(var); if (v == null) { - v = JassIm.ImVar(e, JassIm.ImArrayType(var.getType()), var.getName(), false); - tr.imProg().getGlobals().add(v); + v = JassIm.ImVar(e, var.getType(), var.getName(), false); + c.getFields().add(v); closureVars.put(var, v); } return v; @@ -229,16 +307,21 @@ private FuncDef getSuperMethod() { } - private ImClass getSuperClass() { - WurstType t = e.attrExpectedTyp(); - if (t instanceof WurstTypeInterface) { - WurstTypeInterface it = (WurstTypeInterface) t; - return tr.getClassFor(it.getDef()); - } else if (t instanceof WurstTypeClass) { - WurstTypeClass ct = (WurstTypeClass) t; - return tr.getClassFor(ct.getDef()); + private ImClassType getSuperClass() { + // since the expected type is just an approximation, we calculate the exact type here again: + WurstTypeClassOrInterface t = (WurstTypeClassOrInterface) e.attrExpectedTyp(); + ClassOrInterface classDef = t.getDef(); + t = (WurstTypeClassOrInterface) classDef.attrTyp(); + fj.data.List typeParameters = fj.data.List.iterableList(classDef.getTypeParameters()); + VariableBinding mapping = VariableBinding.emptyMapping() + .withTypeVariables(typeParameters); + WurstType closureType = e.attrTyp(); + VariableBinding mapping2 = closureType.matchAgainstSupertype(t, e, mapping, VariablePosition.RIGHT); + if (mapping2 == null) { + throw new CompileError(e, "Could not translate closure: type " + closureType + " does not match " + t); } - throw new CompileError(e.getSource(), "Could not get super class for closure"); + WurstType t2 = t.setTypeArgs(mapping2); + return (ImClassType) t2.imTranslateType(tr); } 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 01d9609a3..74c9c1301 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,10 +46,11 @@ private void removeCycle(List funcs) { de.peeeq.wurstscript.ast.Element trace = funcs.get(0).getTrace(); - ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(), "funcChoice", false); + ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(tr), "funcChoice", false); List flags = Lists.newArrayList(); - ImFunction newFunc = JassIm.ImFunction(trace, makeName(funcs), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), flags); + + ImFunction newFunc = JassIm.ImFunction(trace, makeName(funcs), JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), flags); prog.getFunctions().add(newFunc); newFunc.getParameters().add(choiceVar); newFunc.getParameters().addAll(newParameters); @@ -121,13 +122,8 @@ private void replaceCalls(List funcs, ImFunction newFunc, MapemptyList()); + + ImFunction proxyFunc = JassIm.ImFunction(f.attrTrace(), f.getName() + "_proxy", JassIm.ImTypeVars(), f.getParameters().copy(), (ImType) f.getReturnType().copy(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); prog.getFunctions().add(proxyFunc); ImExprs arguments = JassIm.ImExprs(); @@ -135,11 +131,7 @@ private void replaceCalls(List funcs, ImFunction newFunc, Map funcs, ImFunction newFunc, Map annotatedFunctions) { ImStmts statements = JassIm.ImStmts(); for (ImFunction f : annotatedFunctions.get(calledAnnotation(fc))) { - statements.add(JassIm.ImFunctionCall(fc.getTrace(), f, JassIm.ImExprs(), false, CallType.NORMAL)); + statements.add(JassIm.ImFunctionCall(fc.getTrace(), f, JassIm.ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL)); } fc.replaceBy(ImHelper.statementExprVoid(statements)); } 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 478a69648..4f3c52136 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 @@ -7,6 +7,8 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher; +import de.peeeq.wurstscript.translation.imtojass.TypeRewriter; import de.peeeq.wurstscript.types.TypesHelper; import de.peeeq.wurstscript.utils.Pair; @@ -15,6 +17,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static de.peeeq.wurstscript.types.TypesHelper.imInt; + /** * eliminate classes and dynamic method invocations */ @@ -34,24 +38,58 @@ public EliminateClasses(ImTranslator tr, ImProg prog, boolean checkedDispatch) { } public void eliminateClasses() { - + moveFunctionsOutOfClasses(); for (ImClass c : prog.getClasses()) { eliminateClass(c); } + // for each method, create a dispatch function + for (ImMethod m : prog.getMethods()) { + ImClass c = m.getMethodClass().getClassDef(); + createDispatchFunc(c, m); + } + for (ImFunction f : prog.getFunctions()) { eliminateClassRelatedExprs(f); } prog.getClasses().clear(); + prog.getMethods().clear(); + + eliminateClassTypes(); + } + + private void eliminateClassTypes() { + TypeRewriter.rewriteTypes(prog, this::eliminateClassTypes); } + private ImType eliminateClassTypes(ImType imType) { + return imType.match(new TypeRewriteMatcher() { + @Override + public ImType case_ImClassType(ImClassType t) { + return imInt(); + } + }); + } + + + /** + * Move all the functions out of classes and into the global program + */ + private void moveFunctionsOutOfClasses() { + for (ImClass c : prog.getClasses()) { + prog.getFunctions().addAll(c.getFunctions().removeAll()); + } + } + + 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(), JassIm.ImArrayType(f.getType()), f.getName(), false); + .ImVar(f.getTrace(), type, f.getName(), false); prog.getGlobals().add(v); fieldToArray.put(f, v); } @@ -81,7 +119,7 @@ public void createDispatchFunc(ImClass c, ImMethod m) { } - ImFunction df = JassIm.ImFunction(m.getTrace(), "dispatch_" + c.getName() + "_" + m.getName(), m + ImFunction df = JassIm.ImFunction(m.getTrace(), "dispatch_" + c.getName() + "_" + m.getName(), JassIm.ImTypeVars(), m .getImplementation().getParameters().copy(), m .getImplementation().getReturnType(), JassIm.ImVars(), JassIm .ImStmts(), flags); @@ -170,7 +208,7 @@ private void createDispatch(ImFunction df, ImStmts stmts, ImVar resultVar, } // only one method, call it ImFunctionCall call = JassIm.ImFunctionCall(df.getTrace(), ranges - .get(start).getB().getImplementation(), arguments, false, CallType.NORMAL); + .get(start).getB().getImplementation(), JassIm.ImTypeArguments(), arguments, false, CallType.NORMAL); if (resultVar == null) { stmts.add(call); } else { @@ -332,26 +370,26 @@ public void visit(ImTypeIdOfObj e) { } private void replaceTypeIdOfObj(ImTypeIdOfObj e) { - ImVar typeIdVar = translator.getClassManagementVarsFor(e.getClazz()).typeId; + ImVar typeIdVar = translator.getClassManagementVarsFor(e.getClazz().getClassDef()).typeId; ImExpr obj = e.getObj(); obj.setParent(null); e.replaceBy(JassIm.ImVarArrayAccess(e.attrTrace(), typeIdVar, JassIm.ImExprs(obj))); } private void replaceTypeIdOfClass(ImTypeIdOfClass e) { - e.replaceBy(JassIm.ImIntVal(e.getClazz().attrTypeId())); + e.replaceBy(JassIm.ImIntVal(e.getClazz().getClassDef().attrTypeId())); } private void replaceInstanceof(ImInstanceof e) { ImFunction f = e.getNearestFunc(); - List allSubClasses = getAllSubclasses(e.getClazz()); + List allSubClasses = getAllSubclasses(e.getClazz().getClassDef()); List subClassIds = allSubClasses.stream() .map(ImClass::attrTypeId) .collect(Collectors.toList()); List idRanges = IntRange.createFromIntList(subClassIds); ImExpr obj = e.getObj(); obj.setParent(null); - ImVar typeIdVar = translator.getClassManagementVarsFor(e.getClazz()).typeId; + ImVar typeIdVar = translator.getClassManagementVarsFor(e.getClazz().getClassDef()).typeId; ImExpr objTypeId = JassIm.ImVarArrayAccess(e.attrTrace(), typeIdVar, JassIm.ImExprs(obj)); @@ -360,7 +398,7 @@ private void replaceInstanceof(ImInstanceof e) { ImExpr objTypeIdExpr = objTypeId; if (useTempVar) { // use temporary variable - tempVar = JassIm.ImVar(e.attrTrace(), TypesHelper.imInt(), "instanceOfTemp", false); + tempVar = JassIm.ImVar(e.attrTrace(), imInt(), "instanceOfTemp", false); f.getLocals().add(tempVar); objTypeIdExpr = JassIm.ImVarAccess(tempVar); } @@ -407,16 +445,16 @@ private void getAllSubclassesH(List result, ImClass clazz) { } private void replaceDealloc(ImDealloc e) { - ImFunction deallocFunc = translator.deallocFunc.getFor(e.getClazz()); + ImFunction deallocFunc = translator.deallocFunc.getFor(e.getClazz().getClassDef()); ImExpr obj = e.getObj(); obj.setParent(null); - e.replaceBy(JassIm.ImFunctionCall(e.attrTrace(), deallocFunc, JassIm.ImExprs(obj), false, CallType.NORMAL)); + e.replaceBy(JassIm.ImFunctionCall(e.attrTrace(), deallocFunc, JassIm.ImTypeArguments(), JassIm.ImExprs(obj), false, CallType.NORMAL)); } private void replaceAlloc(ImAlloc e) { - ImFunction allocFunc = translator.allocFunc.getFor(e.getClazz()); - e.replaceBy(JassIm.ImFunctionCall(e.attrTrace(), allocFunc, JassIm.ImExprs(), false, CallType.NORMAL)); + ImFunction allocFunc = translator.allocFunc.getFor(e.getClazz().getClassDef()); + e.replaceBy(JassIm.ImFunctionCall(e.attrTrace(), allocFunc, JassIm.ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL)); } private void replaceMethodCall(ImMethodCall mc) { @@ -430,8 +468,7 @@ private void replaceMethodCall(ImMethodCall mc) { if (dispatch == null) { throw new CompileError(mc.attrTrace().attrSource(), "Could not find dispatch for " + mc.getMethod().getName()); } - mc.replaceBy(JassIm.ImFunctionCall(mc.getTrace(), - dispatch, arguments, false, CallType.NORMAL)); + mc.replaceBy(JassIm.ImFunctionCall(mc.getTrace(), dispatch, JassIm.ImTypeArguments(), arguments, false, CallType.NORMAL)); } @@ -439,7 +476,13 @@ private void replaceMemberAccess(ImMemberAccess ma) { ImExpr receiver = ma.getReceiver(); receiver.setParent(null); - ma.replaceBy(JassIm.ImVarArrayAccess(ma.attrTrace(), fieldToArray.get(ma.getVar()), JassIm.ImExprs(receiver))); + ImVar fieldArray = fieldToArray.get(ma.getVar()); + if (fieldArray == null) { + throw new CompileError(ma, "Could not find field array for " + ma); + } + ImExprs indexes = JassIm.ImExprs(receiver); + indexes.addAll(ma.getIndexes().removeAll()); + ma.replaceBy(JassIm.ImVarArrayAccess(ma.attrTrace(), fieldArray, indexes)); } 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 new file mode 100644 index 000000000..a7fddc911 --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java @@ -0,0 +1,694 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Table; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtojass.ImAttrType; +import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +/** + * eliminate classes and dynamic method invocations + */ +public class EliminateGenerics { + + private final ImTranslator translator; + private final ImProg prog; + // TODO only use one queue here with the different cases (add: generic class type, member access) + private Deque genericsUses = new ArrayDeque<>(); + private Table specializedFunctions = HashBasedTable.create(); + private Table specializedMethods = HashBasedTable.create(); + private Table specializedClasses = HashBasedTable.create(); + private Multimap> onSpecializedClassTriggers = HashMultimap.create(); + + public EliminateGenerics(ImTranslator tr, ImProg prog) { + translator = tr; + this.prog = prog; + } + + + public void transform() { + + simplifyClasses(); + + addMemberTypeArguments(); + + collectGenericUsages(); + + eliminateGenericUses(); + + removeGenericConstructs(); + + } + + private void onSpecializeClass(ImClass orig, BiConsumer action) { + onSpecializedClassTriggers.put(orig, action); + specializedClasses.row(orig).forEach(action); + } + + private void addMemberTypeArguments() { + prog.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImMethodCall mc) { + super.visit(mc); + handle(mc, mc.getMethod().attrClass()); + } + + @Override + public void visit(ImMemberAccess ma) { + super.visit(ma); + handle(ma, (ImClass) ma.getVar().getParent().getParent()); + } + + private void handle(ImMemberOrMethodAccess ma, ImClass owningClass) { + ImType receiverType = ma.getReceiver().attrTyp(); + if (!(receiverType instanceof ImClassType)) { + // using old generics + return; + } + ImClassType rt = (ImClassType) receiverType; + ImClassType ct = adaptToSuperclass(rt, owningClass); + if (ct == null) { + throw new CompileError(ma, "Could not adapt receiver " + rt + " to superclass " + owningClass + " in member access " + ma); + } + List typeArgs = ct.getTypeArguments().stream().map(ImTypeArgument::copy).collect(Collectors.toList()); + ma.getTypeArguments().addAll(0, typeArgs); + } + + }); + } + + private static ImClassType adaptToSuperclass(ImClassType ct, ImClass owningClass) { + if (ct.getClassDef() == owningClass) { + return ct; + } + for (ImClassType sc : superTypes(ct)) { + ImClassType r = adaptToSuperclass(sc, owningClass); + if (r != null) { + return r; + } + } + return null; + } + + 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)) + .iterator(); + } + + + /** + * Removed methods and functions from classes and adds them to the + * main program. + */ + private void simplifyClasses() { + for (ImClass c : prog.getClasses()) { + simplifyClass(c); + } + } + + + private void simplifyClass(ImClass c) { + moveMethodsOutOfClass(c); + moveFunctionsOutOfClass(c); + + } + + private void moveMethodsOutOfClass(ImClass c) { + List methods = c.getMethods().removeAll(); + prog.getMethods().addAll(methods); + } + + private void moveFunctionsOutOfClass(ImClass c) { + List functions = c.getFunctions().removeAll(); + for (ImFunction f : functions) { + prog.getFunctions().add(f); + + List newTypeVars = c.getTypeVariables() + .stream() + .map(ImTypeVar::copy) + .collect(Collectors.toList()); + f.getTypeVariables().addAll(0, newTypeVars); + List typeArgs = newTypeVars + .stream() + .map(ta -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap())) + .collect(Collectors.toList()); + rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables()); + } + } + + + /** + * When everything is specialized, we can remove generic functions and classes + */ + private void removeGenericConstructs() { + prog.getFunctions().removeIf(f -> !f.getTypeVariables().isEmpty()); + prog.getMethods().removeIf(m -> !m.getImplementation().getTypeVariables().isEmpty()); + prog.getClasses().removeIf(c -> !c.getTypeVariables().isEmpty()); + for (ImClass c : prog.getClasses()) { + c.getFields().removeIf(f -> isGenericType(f.getType())); + } + } + + private void eliminateGenericUses() { + while (!genericsUses.isEmpty()) { + GenericUse gu = genericsUses.removeFirst(); + gu.eliminate(); + } + } + + /** + * creates a specialized version of this function + */ + private ImFunction specializeFunction(ImFunction f, GenericTypes generics) { + ImFunction specialized = specializedFunctions.get(f, generics); + if (specialized != null) { + return specialized; + } + if (f.getTypeVariables().isEmpty()) { + return f; + } + if (generics.containsTypeVariable()) { + throw new CompileError(f, "Generics should not contain type variables"); + } + + ImFunction newF = f.copyWithRefs(); + specializedFunctions.put(f, generics, newF); + prog.getFunctions().add(newF); + newF.getTypeVariables().removeAll(); + List typeVars = f.getTypeVariables(); + + newF.setName(f.getName() + "⟪" + generics.makeName() + "⟫"); + rewriteGenerics(newF, generics, typeVars); + collectGenericUsages(newF); + return newF; + } + + /** + * creates a specialized version of this method + */ + private ImMethod specializeMethod(ImMethod m, GenericTypes generics) { + + ImMethod specialized = specializedMethods.get(m, generics); + if (specialized != null) { + return specialized; + } + if (generics.containsTypeVariable()) { + throw new CompileError(m, "Generics should not contain type variables."); + } + + ImMethod newM = m.copyWithRefs(); + specializedMethods.put(m, generics, newM); + prog.getMethods().add(newM); + + ImClassType newClassType = newM.getMethodClass().copy(); + for (int i = 0; i < newClassType.getTypeArguments().size(); i++) { + newClassType.getTypeArguments().set(i, generics.getTypeArguments().get(i).copy()); + } + newM.setMethodClass(specializeType(newClassType)); + + newM.setName(m.getName() + "⟪" + generics.makeName() + "⟫"); + newM.setImplementation(specializeFunction(newM.getImplementation(), generics)); + adaptSubmethods(m.getSubMethods(), newM); + return newM; + } + + private void adaptSubmethods(List oldSubMethods, ImMethod newM) { + newM.setSubMethods(new ArrayList<>()); + ImClassType newClassT = newM.getMethodClass(); + ImClass newMClass = newClassT.getClassDef(); + for (ImMethod subMethod : oldSubMethods) { + ImClassType subClassT = subMethod.getMethodClass(); + ImClass subClass = subClassT.getClassDef(); + if (isGenericType(subClassT)) { + onSpecializeClass(subClass, (subGenerics, specializedSubClass) -> { + if (specializedSubClass.isSubclassOf(newMClass)) { + ImMethod specializedSubMethod = specializeMethod(subMethod, subGenerics); + newM.getSubMethods().add(specializedSubMethod); + } + }); + } else { + subClass.getSuperClasses().replaceAll(this::specializeType); + ImClassType newClassTspecialized = specializeType(newClassT); + if (subClass.isSubclassOf(newClassTspecialized.getClassDef())) { + newM.getSubMethods().add(subMethod); + } + } + } + } + + /** + * Replaces all uses of the given typeVars with the type arguments given in parameter generics. + */ + private void rewriteGenerics(Element element, GenericTypes generics, List typeVars) { + if (generics.getTypeArguments().size() != typeVars.size()) { + throw new RuntimeException("Rewrite generics with wrong sizes\n" + + "generics: " + generics + "\n" + + "typevars: " + typeVars + "\n" + + "in\n: " + element); + } + element.accept(new Element.DefaultVisitor() { + + @Override + public void visit(ImClass c) { + c.getSuperClasses().replaceAll(t -> (ImClassType) transformType(t, generics, typeVars)); + super.visit(c); + } + + @Override + public void visit(ImTypeArgument ta) { + ta.setType(transformType(ta.getType(), generics, typeVars)); + } + + @Override + public void visit(ImNull e) { + e.setType(transformType(e.getType(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImFunction e) { + e.setReturnType(transformType(e.getReturnType(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImVar e) { + e.setType(transformType(e.getType(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImAlloc e) { + e.setClazz((ImClassType) transformType(e.getClazz(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImInstanceof e) { + e.setClazz((ImClassType) transformType(e.getClazz(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImTypeIdOfClass e) { + e.setClazz((ImClassType) transformType(e.getClazz(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImTypeIdOfObj e) { + e.setClazz((ImClassType) transformType(e.getClazz(), generics, typeVars)); + super.visit(e); + } + + @Override + public void visit(ImDealloc e) { + e.setClazz((ImClassType) transformType(e.getClazz(), generics, typeVars)); + super.visit(e); + } + + }); + } + + private static ImType transformType(ImType type, GenericTypes generics, List typeVars) { + return ImAttrType.substituteType(type, generics.getTypeArguments(), typeVars); + } + + /** + * creates a specialized version of this class + */ + private ImClass specializeClass(ImClass c, GenericTypes generics) { + if (c.getTypeVariables().isEmpty()) { + return c; + } + ImClass specialized = specializedClasses.get(c, generics); + if (specialized != null) { + return specialized; + } + if (generics.containsTypeVariable()) { + throw new CompileError(c, "Generics should not contain type variables."); + } + ImClass newC = c.copyWithRefs(); + newC.setSuperClasses(new ArrayList<>(newC.getSuperClasses())); + specializedClasses.put(c, generics, newC); + prog.getClasses().add(newC); + newC.getTypeVariables().removeAll(); + + newC.setName(c.getName() + "⟪" + generics.makeName() + "⟫"); + List typeVars = c.getTypeVariables(); + rewriteGenerics(newC, generics, typeVars); + newC.getSuperClasses().replaceAll(this::specializeType); + // we don't collect generic usages to avoid infinite loops + // in cases like class C { C> x; } + onSpecializedClassTriggers.get(c).forEach(consumer -> + consumer.accept(generics, newC)); + return newC; + } + + + /** + * Collects all usages from non-generic functions + */ + private void collectGenericUsages() { + collectGenericUsages(prog); + } + + private void collectGenericUsages(Element element) { + element.accept(new Element.DefaultVisitor() { + @Override + public void visit(ImFunctionCall f) { + super.visit(f); + if (!f.getTypeArguments().isEmpty()) { + genericsUses.add(new GenericImFunctionCall(f)); + } + } + + @Override + public void visit(ImMethodCall mc) { + super.visit(mc); + if (!mc.getTypeArguments().isEmpty()) { + genericsUses.add(new GenericMethodCall(mc)); + } + } + + @Override + public void visit(ImMemberAccess ma) { + super.visit(ma); + if (!ma.getTypeArguments().isEmpty()) { + genericsUses.add(new GenericMemberAccess(ma)); + } + + } + + @Override + 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."); + } + genericsUses.add(new GenericVar(v)); + } + } + + @Override + public void visit(ImClass c) { + if (!c.getTypeVariables().isEmpty()) { + // handle generic classes after they are specialized + return; + } + genericsUses.add(() -> { + List newSuperClasses = c.getSuperClasses().stream().map(EliminateGenerics.this::specializeType).collect(Collectors.toList()); + c.setSuperClasses(newSuperClasses); + }); + + super.visit(c); + } + + @Override + public void visit(ImFunction f) { + if (!f.getTypeVariables().isEmpty()) { + // handle generic functions after they are specialized + return; + } + + super.visit(f); + if (isGenericType(f.getReturnType())) { + genericsUses.add(new GenericReturnTypeFunc(f)); + } + } + + @Override + public void visit(ImAlloc f) { + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + + @Override + public void visit(ImDealloc f) { + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + + @Override + public void visit(ImInstanceof f) { + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + + @Override + public void visit(ImTypeIdOfObj f) { + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + + @Override + public void visit(ImTypeIdOfClass f) { + if (isGenericType(f.getClazz())) { + genericsUses.add(new GenericClazzUse(f)); + } + } + + }); + } + + static boolean isGenericType(ImType type) { + return type.match(new ImType.Matcher() { + @Override + public Boolean case_ImArrayTypeMulti(ImArrayTypeMulti t) { + return isGenericType(t.getEntryType()); + } + + @Override + public Boolean case_ImArrayType(ImArrayType t) { + return isGenericType(t.getEntryType()); + } + + @Override + public Boolean case_ImClassType(ImClassType t) { + return !t.getTypeArguments().isEmpty(); + } + + @Override + public Boolean case_ImVoid(ImVoid t) { + return false; + } + + @Override + public Boolean case_ImTupleType(ImTupleType t) { + for (ImType tt : t.getTypes()) { + if (isGenericType(tt)) { + return true; + } + } + return false; + } + + @Override + public Boolean case_ImSimpleType(ImSimpleType t) { + return false; + } + + @Override + public Boolean case_ImTypeVarRef(ImTypeVarRef t) { + return false; + } + }); + } + + static boolean containsTypeVariable(ImType type) { + return type.match(new ImType.Matcher() { + @Override + public Boolean case_ImArrayTypeMulti(ImArrayTypeMulti t) { + return containsTypeVariable(t.getEntryType()); + } + + @Override + public Boolean case_ImArrayType(ImArrayType t) { + return containsTypeVariable(t.getEntryType()); + } + + @Override + public Boolean case_ImClassType(ImClassType t) { + return t.getTypeArguments().stream() + .anyMatch(tt -> containsTypeVariable(tt.getType())); + } + + @Override + public Boolean case_ImVoid(ImVoid t) { + return false; + } + + @Override + public Boolean case_ImTupleType(ImTupleType t) { + for (ImType tt : t.getTypes()) { + if (containsTypeVariable(tt)) { + return true; + } + } + return false; + } + + @Override + public Boolean case_ImSimpleType(ImSimpleType t) { + return false; + } + + @Override + public Boolean case_ImTypeVarRef(ImTypeVarRef t) { + return true; + } + }); + } + + interface GenericUse { + + void eliminate(); + } + + class GenericImFunctionCall implements GenericUse { + private final ImFunctionCall fc; + + GenericImFunctionCall(ImFunctionCall fc) { + this.fc = fc; + } + + @Override + public void eliminate() { + ImFunction f = fc.getFunc(); + + GenericTypes generics = new GenericTypes(specializeTypeArgs(fc.getTypeArguments())); + ImFunction specializedFunc = specializedFunctions.get(f, generics); + if (specializedFunc == null) { + specializedFunc = specializeFunction(f, generics); + } + fc.setFunc(specializedFunc); + fc.getTypeArguments().removeAll(); + } + } + + class GenericMethodCall implements GenericUse { + private final ImMethodCall mc; + + GenericMethodCall(ImMethodCall mc) { + this.mc = mc; + } + + @Override + public void eliminate() { + ImMethod f = mc.getMethod(); + + GenericTypes generics = new GenericTypes(specializeTypeArgs(mc.getTypeArguments())); + ImMethod specializedMethod = specializeMethod(f, generics); + mc.setMethod(specializedMethod); + mc.getTypeArguments().removeAll(); + } + } + + class GenericMemberAccess implements GenericUse { + private final ImMemberAccess ma; + + GenericMemberAccess(ImMemberAccess ma) { + this.ma = ma; + } + + @Override + public void eliminate() { + ImVar f = ma.getVar(); + ImClass owningClass = (ImClass) f.getParent().getParent(); + GenericTypes generics = new GenericTypes(specializeTypeArgs(ma.getTypeArguments())); + ImClass specializedClass = specializeClass(owningClass, generics); + int fieldIndex = owningClass.getFields().indexOf(f); + ImVar newVar = specializedClass.getFields().get(fieldIndex); + ma.setVar(newVar); + ma.getTypeArguments().removeAll(); + newVar.setType(specializeType(newVar.getType())); + } + } + + + class GenericVar implements GenericUse { + private final ImVar mc; + + GenericVar(ImVar mc) { + this.mc = mc; + } + + @Override + public void eliminate() { + mc.setType(specializeType(mc.getType())); + } + } + + private ImClassType specializeType(ImClassType type) { + return (ImClassType) specializeType((ImType) type); + } + + private ImType specializeType(ImType type) { + return type.match(new TypeRewriteMatcher() { + + @Override + public ImType case_ImClassType(ImClassType t) { + ImTypeArguments typeArgs = t.getTypeArguments(); + List newTypeArgs = specializeTypeArgs(typeArgs); + ImClass specializedClass = specializeClass(t.getClassDef(), new GenericTypes(newTypeArgs)); + return JassIm.ImClassType(specializedClass, JassIm.ImTypeArguments()); + } + + }); + } + + @NotNull + private List specializeTypeArgs(ImTypeArguments typeArgs) { + return typeArgs + .stream() + .map(ta -> JassIm.ImTypeArgument(specializeType(ta.getType()), ta.getTypeClassBinding())) + .collect(Collectors.toList()); + } + + class GenericReturnTypeFunc implements GenericUse { + private final ImFunction mc; + + GenericReturnTypeFunc(ImFunction mc) { + this.mc = mc; + } + + @Override + public void eliminate() { + mc.setReturnType(specializeType(mc.getReturnType())); + } + } + + private class GenericClazzUse implements GenericUse { + private final ImClassRelatedExprWithClass f; + + public GenericClazzUse(ImClassRelatedExprWithClass f) { + super(); + this.f = f; + } + + @Override + public void eliminate() { + f.setClazz(specializeType(f.getClazz())); + } + } + +} 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 f8b543267..b34ad71f7 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 @@ -15,8 +15,12 @@ import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; +import fj.data.Either; +import fj.data.Option; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static de.peeeq.wurstscript.jassIm.JassIm.*; @@ -120,10 +124,10 @@ static ImExpr wrapTranslation(Element trace, ImTranslator t, ImExpr translated, return translated; } else if (fromIndex != null) { // System.out.println(" --> fromIndex"); - return JassIm.ImFunctionCall(trace, fromIndex, JassIm.ImExprs(translated), false, CallType.NORMAL); + return ImFunctionCall(trace, fromIndex, ImTypeArguments(), JassIm.ImExprs(translated), false, CallType.NORMAL); } else if (toIndex != null) { // System.out.println(" --> toIndex"); - return JassIm.ImFunctionCall(trace, toIndex, JassIm.ImExprs(translated), false, CallType.NORMAL); + return ImFunctionCall(trace, toIndex, ImTypeArguments(), JassIm.ImExprs(translated), false, CallType.NORMAL); } return translated; } @@ -135,7 +139,7 @@ public static ImExpr translateIntern(ExprBinary e, ImTranslator t, ImFunction f) if (e.attrFuncLink() != null) { // overloaded operator ImFunction calledFunc = t.getFuncFor(e.attrFuncDef()); - return JassIm.ImFunctionCall(e, calledFunc, ImExprs(left, right), false, CallType.NORMAL); + return ImFunctionCall(e, calledFunc, ImTypeArguments(), ImExprs(left, right), false, CallType.NORMAL); } if (op == WurstOperator.DIV_REAL) { if (Utils.isJassCode(e)) { @@ -169,7 +173,7 @@ public static ImExpr translateIntern(ExprBoolVal e, ImTranslator t, ImFunction f public static ImExpr translateIntern(ExprFuncRef e, ImTranslator t, ImFunction f) { ImFunction func = t.getFuncFor(e.attrFuncDef()); - return ImFuncRef(func); + return ImFuncRef(e, func); } public static ImExpr translateIntern(ExprIntVal e, ImTranslator t, ImFunction f) { @@ -188,7 +192,7 @@ public static ImExpr translateIntern(ExprNull e, ImTranslator t, ImFunction f) { } else if (expectedTypeRaw instanceof WurstTypeUnknown) { e.addError("Cannot use 'null' in this context."); } - return ImNull(expectedTypeRaw.imTranslateType()); + return ImNull(expectedTypeRaw.imTranslateType(t)); } public static ImExpr translateIntern(ExprRealVal e, ImTranslator t, ImFunction f) { @@ -245,11 +249,10 @@ private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f) if (e instanceof AstElementWithIndexes) { ImExpr index1 = implicitParam.imTranslateExpr(t, f); ImExpr index2 = ((AstElementWithIndexes) e).getIndexes().get(0).imTranslateExpr(t, f); - return JassIm.ImVarArrayAccess(e, v, JassIm.ImExprs(index1, index2)); - + return JassIm.ImMemberAccess(e, index1, JassIm.ImTypeArguments(), v, JassIm.ImExprs(index2)); } else { ImExpr index = implicitParam.imTranslateExpr(t, f); - return ImVarArrayAccess(e, v, JassIm.ImExprs(index)); + return JassIm.ImMemberAccess(e, index, JassIm.ImTypeArguments(), v, JassIm.ImExprs()); } } else { // direct var access @@ -391,7 +394,9 @@ private static int tupleSize(WurstType t) { } public static ImExpr translateIntern(ExprCast e, ImTranslator t, ImFunction f) { - return e.getExpr().imTranslateExpr(t, f); + ImExpr et = e.getExpr().imTranslateExpr(t, f); + ImType toType = e.getTyp().attrTyp().imTranslateType(t); + return JassIm.ImCast(et, toType); } public static ImExpr translateIntern(FunctionCall e, ImTranslator t, ImFunction f) { @@ -415,7 +420,7 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu String exFunc = s.getValS(); NameLink func = Utils.getFirst(e.lookupFuncs(exFunc)); ImFunction executedFunc = t.getFuncFor((TranslatedToImFunction) func.getDef()); - return JassIm.ImFunctionCall(e, executedFunc, JassIm.ImExprs(), true, CallType.EXECUTE); + return ImFunctionCall(e, executedFunc, ImTypeArguments(), JassIm.ImExprs(), true, CallType.EXECUTE); } if (e.getFuncName().equals("compiletime") @@ -486,22 +491,26 @@ 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(), "receiver", false); + tempVar = JassIm.ImVar(leftExpr, leftExpr.attrTyp().imTranslateType(t), "receiver", false); f.getLocals().add(tempVar); stmts = JassIm.ImStmts(ImSet(e, ImVarAccess(tempVar), receiver)); receiver = JassIm.ImVarAccess(tempVar); } + + ImExpr call; if (dynamicDispatch) { ImMethod method = t.getMethodFor((FuncDef) calledFunc); - call = JassIm.ImMethodCall(e, method, receiver, imArgs, false); + ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, method.getImplementation().getTypeVariables()); + call = ImMethodCall(e, method, typeArguments, receiver, imArgs, false); } else { ImFunction calledImFunc = t.getFuncFor(calledFunc); if (receiver != null) { imArgs.add(0, receiver); } - call = ImFunctionCall(e, calledImFunc, imArgs, false, CallType.NORMAL); + ImTypeArguments typeArguments = getFunctionCallTypeArguments(t, e.attrFunctionSignature(), e, calledImFunc.getTypeVariables()); + call = ImFunctionCall(e, calledImFunc, typeArguments, imArgs, false, CallType.NORMAL); } if (returnReveiver) { @@ -514,6 +523,27 @@ private static ImExpr translateFunctionCall(FunctionCall e, ImTranslator t, ImFu } } + private static ImTypeArguments getFunctionCallTypeArguments(ImTranslator tr, FunctionSignature sig, Element location, ImTypeVars typeVariables) { + ImTypeArguments res = ImTypeArguments(); + VariableBinding mapping = sig.getMapping(); + for (ImTypeVar tv : typeVariables) { + TypeParamDef tp = tr.getTypeParamDef(tv); + Option to = mapping.get(tp); + if (to.isNone()) { + throw new CompileError(location, "Type variable " + tp.getName() + " not bound in mapping."); + } + WurstTypeBoundTypeParam t = to.some(); + if (!t.isTemplateTypeParameter()) { + continue; + } + ImType type = t.imTranslateType(tr); + // TODO handle constraints + Map> typeClassBinding = new HashMap<>(); + res.add(ImTypeArgument(type, typeClassBinding)); + } + return res; + } + private static boolean isCalledOnDynamicRef(FunctionCall e) { if (e instanceof ExprMemberMethod) { ExprMemberMethod mm = (ExprMemberMethod) e; @@ -539,7 +569,11 @@ public static ImExpr translateIntern(ExprIncomplete e, ImTranslator t, ImFunctio public static ImExpr translateIntern(ExprNewObject e, ImTranslator t, ImFunction f) { ConstructorDef constructorFunc = e.attrConstructorDef(); ImFunction constructorImFunc = t.getConstructNewFunc(constructorFunc); - return ImFunctionCall(e, constructorImFunc, translateExprs(e.getArgs(), t, f), false, CallType.NORMAL); + FunctionSignature sig = e.attrFunctionSignature(); + 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); } public static ImExprOpt translate(NoExpr e, ImTranslator translator, ImFunction f) { @@ -548,27 +582,31 @@ public static ImExprOpt translate(NoExpr e, ImTranslator translator, ImFunction public static ImExpr translateIntern(ExprInstanceOf e, ImTranslator translator, ImFunction f) { WurstType targetType = e.getTyp().attrTyp(); - if (targetType instanceof WurstTypeNamedScope) { - WurstTypeNamedScope t = (WurstTypeNamedScope) targetType; - ImClass clazz = translator.getClassFor((StructureDef) t.getDef()); - return JassIm.ImInstanceof(e.getExpr().imTranslateExpr(translator, f), clazz); + ImType imTargetType = targetType.imTranslateType(translator); + if (imTargetType instanceof ImClassType) { + return JassIm.ImInstanceof(e.getExpr().imTranslateExpr(translator, f), (ImClassType) imTargetType); } throw new Error("Cannot compile instanceof " + targetType); } public static ImExpr translate(ExprTypeId e, ImTranslator translator, ImFunction f) { WurstType leftType = e.getLeft().attrTyp(); - if (leftType instanceof WurstTypeClassOrInterface) { - WurstTypeClassOrInterface wtc = (WurstTypeClassOrInterface) leftType; - - ImClass c = translator.getClassFor(wtc.getDef()); - if (wtc.isStaticRef()) { - return JassIm.ImTypeIdOfClass(c); + ImType imLeftType = leftType.imTranslateType(translator); + if (imLeftType instanceof ImClassType) { + ImClassType imLeftTypeC = (ImClassType) imLeftType; + if (leftType instanceof WurstTypeClassOrInterface) { + WurstTypeClassOrInterface wtc = (WurstTypeClassOrInterface) leftType; + + if (wtc.isStaticRef()) { + return JassIm.ImTypeIdOfClass(imLeftTypeC); + } else { + return JassIm.ImTypeIdOfObj(e.getLeft().imTranslateExpr(translator, f), imLeftTypeC); + } } else { - return JassIm.ImTypeIdOfObj(e.getLeft().imTranslateExpr(translator, f), c); + throw new CompileError(e, "not implemented for " + leftType); } } else { - throw new Error("not implemented for " + leftType); + throw new CompileError(e, "not implemented for " + leftType); } } @@ -615,7 +653,8 @@ public static ImExpr translate(ExprDestroy s, ImTranslator t, ImFunction f) { public static ImExpr destroyClass(ExprDestroy s, ImTranslator t, ImFunction f, StructureDef classDef) { ImMethod destroyFunc = t.destroyMethod.getFor(classDef); - return JassIm.ImMethodCall(s, destroyFunc, s.getDestroyedObj().imTranslateExpr(t, f), ImExprs(), false); + return ImMethodCall(s, destroyFunc, ImTypeArguments(), s.getDestroyedObj().imTranslateExpr(t, f), ImExprs(), false); + } public static ImExpr translate(ExprEmpty s, ImTranslator translator, ImFunction f) { @@ -673,11 +712,11 @@ public static ImLExpr translateLvalue(LExpr e, ImTranslator t, ImFunction f) { if (e instanceof AstElementWithIndexes) { ImExpr index1 = implicitParam.imTranslateExpr(t, f); ImExpr index2 = ((AstElementWithIndexes) e).getIndexes().get(0).imTranslateExpr(t, f); - return JassIm.ImVarArrayAccess(e, v, JassIm.ImExprs(index1, index2)); + return JassIm.ImMemberAccess(e, index1, JassIm.ImTypeArguments(), v, JassIm.ImExprs(index2)); } else { ImExpr index = implicitParam.imTranslateExpr(t, f); - return ImVarArrayAccess(e, v, JassIm.ImExprs(index)); + return JassIm.ImMemberAccess(e, index, JassIm.ImTypeArguments(), v, JassIm.ImExprs()); } } else { // direct var access 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 6d24b331d..ca2cbd125 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 @@ -57,6 +57,14 @@ 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) { + return imCast.getExpr().flatten(translator, f); + } + public static class Result { final List stmts; @@ -245,7 +253,7 @@ public static Result flatten(ImSet s, ImTranslator t, ImFunction f) { public static Result flatten(ImFunctionCall e, ImTranslator t, ImFunction f) { MultiResult r = flattenExprs(t, f, e.getArguments()); - return new Result(r.stmts, JassIm.ImFunctionCall(e.getTrace(), e.getFunc(), ImExprs(r.exprs), e.getTuplesEliminated(), e.getCallType())); + return new Result(r.stmts, ImFunctionCall(e.getTrace(), e.getFunc(), ImTypeArguments(), ImExprs(r.exprs), e.getTuplesEliminated(), e.getCallType())); } public static Result flatten(ImOperatorCall e, ImTranslator t, ImFunction f) { @@ -260,7 +268,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(), "andLeft", false); + ImVar tempVar = JassIm.ImVar(e.attrTrace(), WurstTypeBool.instance().imTranslateType(t), "andLeft", false); f.getLocals().add(tempVar); ImStmts thenBlock = JassIm.ImStmts(); // if left is true then check right @@ -280,7 +288,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(), "andLeft", false); + ImVar tempVar = JassIm.ImVar(trace, WurstTypeBool.instance().imTranslateType(t), "andLeft", false); f.getLocals().add(tempVar); // if left is true then result is ture ImStmts thenBlock = JassIm.ImStmts(ImSet(trace, ImVarAccess(tempVar), JassIm.ImBoolVal(true))); 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 8dd6dcdc9..229478285 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 @@ -48,7 +48,7 @@ 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(), + g = JassIm.ImVar(fr.attrTrace(), WurstTypeCode.instance().imTranslateType(tr), "ref_function_" + func.getName(), false); refs.put(func, g); tr.addGlobalWithInitalizer(g, fr.copy()); 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 366eb78ab..47d36b670 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 @@ -14,17 +14,17 @@ public static void create(ConstructorDef constr, ImTranslator translator, ImFunc public static void create(ExtensionFuncDef funcDef, ImTranslator translator, ImFunction f) { // return type: - f.setReturnType(funcDef.attrReturnTyp().imTranslateType()); + f.setReturnType(funcDef.attrReturnTyp().imTranslateType(translator)); // parameters ImVar thisVar = translator.getThisVar(funcDef); - thisVar.setType(funcDef.getExtendedType().attrTyp().imTranslateType()); + thisVar.setType(funcDef.getExtendedType().attrTyp().imTranslateType(translator)); f.getParameters().add(thisVar); ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); } public static void create(FuncDef funcDef, ImTranslator translator, ImFunction f) { // return type: - f.setReturnType(funcDef.attrReturnTyp().imTranslateType()); + f.setReturnType(funcDef.attrReturnTyp().imTranslateType(translator)); // parameters if (funcDef.attrIsDynamicClassMember()) { ImVar thisVar = translator.getThisVar(funcDef); @@ -37,7 +37,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()); + f.setReturnType(funcDef.attrReturnTyp().imTranslateType(translator)); ImHelper.translateParameters(funcDef.getParameters(), f.getParameters(), translator); } @@ -62,7 +62,7 @@ public static void create(ExprClosure e, ImTranslator tr, ImFunction f) { for (WShortParameter p : e.getShortParameters()) { f.getParameters().add(tr.getVarFor(p)); } - f.setReturnType(e.getImplementation().attrTyp().imTranslateType()); + f.setReturnType(e.getImplementation().attrTyp().imTranslateType(tr)); } 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 new file mode 100644 index 000000000..e1deb20bf --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/GenericTypes.java @@ -0,0 +1,146 @@ +package de.peeeq.wurstscript.translation.imtranslation; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import de.peeeq.wurstscript.jassIm.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * wraps an imType and adds a hashmap and equals method + */ +class GenericTypes { + private final List typeArguments; + + + public GenericTypes(List typeArguments) { + for (ImTypeArgument ta : typeArguments) { + Preconditions.checkArgument(!EliminateGenerics.isGenericType(ta.getType()), "Type arguments must not be generic: " + typeArguments); + } + this.typeArguments = ImmutableList.copyOf(typeArguments); + } + + public List getTypeArguments() { + return typeArguments; + } + + @Override + public boolean equals(Object o) { + if (o instanceof GenericTypes) { + GenericTypes ot = (GenericTypes) o; + if (typeArguments.size() != ot.typeArguments.size()) { + return false; + } + for (int i = 0; i < typeArguments.size(); i++) { + ImTypeArgument t1 = typeArguments.get(i); + ImTypeArgument t2 = ot.typeArguments.get(i); + if (!t1.getType().equalsType(t2.getType())) { + return false; + } + if (!t1.getTypeClassBinding().equals(t2.getTypeClassBinding())) { + return false; + } + } + return true; + } + return false; + } + + @Override + public int hashCode() { + int res = 7; + for (ImTypeArgument it : typeArguments) { + res = 131 * res + hashType(it.getType()); + } + return res; + } + + private static int hashType(ImType t) { + return t.match(new ImType.Matcher() { + @Override + public Integer case_ImTupleType(ImTupleType t) { + int res = 172; + for (ImType it : t.getTypes()) { + res = 73 * res + hashType(it); + } + return res; + } + + @Override + public Integer case_ImVoid(ImVoid imVoid) { + return 183; + } + + @Override + public Integer case_ImClassType(ImClassType ct) { + int res = ct.getClassDef().hashCode(); + for (ImTypeArgument it : ct.getTypeArguments()) { + res = 73 * res + hashType(it.getType()); + } + return res; + } + + @Override + public Integer case_ImArrayTypeMulti(ImArrayTypeMulti t) { + return 9931532 + hashType(t.getEntryType()); + } + + @Override + public Integer case_ImSimpleType(ImSimpleType t) { + return 234312 + t.getTypename().hashCode(); + } + + @Override + public Integer case_ImArrayType(ImArrayType t) { + return 91532 + hashType(t.getEntryType()); + } + + @Override + public Integer case_ImTypeVarRef(ImTypeVarRef t) { + return t.getTypeVariable().hashCode(); + } + }); + } + + public String makeName() { + StringBuilder sb = new StringBuilder(); + for (ImTypeArgument ta : typeArguments) { + if (sb.length() > 0) { + sb.append(", "); + } + ta.getType().print(sb, 0); + } + return sb.toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("<"); + for (ImTypeArgument ta : typeArguments) { + if (sb.length() > 1) { + sb.append(", "); + } + ta.getType().print(sb, 0); + } + sb.append(">"); + return sb.toString(); + } + + public GenericTypes take(int n) { + return new GenericTypes( + typeArguments.stream().limit(n).collect(Collectors.toList()) + ); + } + + public GenericTypes drop(int n) { + return new GenericTypes( + typeArguments.stream().skip(n).collect(Collectors.toList()) + ); + } + + public boolean containsTypeVariable() { + return typeArguments.stream() + .anyMatch(ta -> EliminateGenerics.containsTypeVariable(ta.getType())); + } +} 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 d870b88aa..52b7ccea0 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 @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.ast.WParameters; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.utils.Constants; import java.util.ArrayList; import java.util.List; @@ -17,28 +18,18 @@ static void translateParameters(WParameters params, ImVars result, ImTranslator } } -// static ImVar translateParam(WParameter p) { -// return tra -// return JassIm.ImVar(p.attrTyp().imTranslateType(), p.getName()); -// } - public static ImType toArray(ImType t) { - if (t instanceof ImSimpleType) { - ImSimpleType imSimpleType = (ImSimpleType) t; - return JassIm.ImArrayType(imSimpleType); - } else if (t instanceof ImTupleType) { - ImTupleType imTupleType = (ImTupleType) t; - return JassIm.ImArrayType(imTupleType); - } else if (t instanceof ImArrayType) { - // already an array + if (t instanceof ImArrayType) { + // already an array (should never happen?) return t; - } else if (t instanceof ImArrayTypeMulti) { + } if (t instanceof ImArrayTypeMulti) { ImArrayTypeMulti mat = ((ImArrayTypeMulti) t); ArrayList nsize = new ArrayList<>(mat.getArraySize()); - nsize.add(8192); + nsize.add(Constants.MAX_ARRAY_SIZE); return JassIm.ImArrayTypeMulti(mat.getEntryType(), nsize); + } else { + return JassIm.ImArrayType(t); } - throw new Error("Can't make array type from " + t); } public static void replaceVar(List stmts, final ImVar oldVar, final ImVar newVar) { @@ -137,7 +128,7 @@ public static ImExpr defaultValueForComplexType(ImType t) { return t.match(new ImType.Matcher() { @Override public ImExpr case_ImArrayTypeMulti(ImArrayTypeMulti imArrayTypeMulti) { - throw new CompileError(t.attrTrace().attrErrorPos(), "Cannot find default value for type " + t); + throw new CompileError(t, "Cannot find default value for type " + t); } @Override @@ -151,12 +142,22 @@ public ImExpr case_ImTupleType(ImTupleType tt) { @Override public ImExpr case_ImArrayType(ImArrayType imArrayType) { - throw new CompileError(t.attrTrace().attrErrorPos(), "Cannot find default value for type " + t); + throw new CompileError(t, "Cannot find default value for type " + t); + } + + @Override + public ImExpr case_ImTypeVarRef(ImTypeVarRef imTypeVarRef) { + throw new CompileError(t, "Cannot find default value for type " + t); } @Override public ImExpr case_ImVoid(ImVoid imVoid) { - throw new CompileError(t.attrTrace().attrErrorPos(), "Cannot find default value for type " + t); + throw new CompileError(t, "Cannot find default value for type " + t); + } + + @Override + public ImExpr case_ImClassType(ImClassType imClassType) { + return JassIm.ImIntVal(0); } @Override 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 ac9525159..5219ab80e 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 @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; public class ImPrinter { @@ -18,45 +19,67 @@ public static void print(ImProg p, Appendable sb, int indent) { f.print(sb, indent); append(sb, "\n"); } + for (ImMethod m : p.getMethods()) { + printMethod(sb, m, indent); + } append(sb, "\n\n"); for (ImClass c : p.getClasses()) { - append(sb, "class "); - append(sb, c.getName()); - append(sb, smallHash(c)); - append(sb, " extends "); - for (ImClass sc : c.getSuperClasses()) { - append(sb, sc.getName()); - append(sb, smallHash(sc)); - append(sb, " "); - } - append(sb, "{\n"); - for (ImVar f : c.getFields()) { - f.print(sb, 1); - append(sb, "\n"); - } - append(sb, "\n\n"); - for (ImMethod m : c.getMethods()) { - if (m.getIsAbstract()) { - append(sb, " abstract"); - } - append(sb, " method "); - append(sb, m.getName()); - append(sb, smallHash(m)); - append(sb, " implemented by "); - append(sb, m.getImplementation().getName()); - append(sb, "\n"); - for (ImMethod sm : m.getSubMethods()) { - append(sb, " sub: "); - append(sb, sm.getName()); - append(sb, smallHash(sm)); - append(sb, "\n"); - } - append(sb, "\n"); - } + print(c, sb, indent); + } - append(sb, "}\n\n"); + } + + public static void print(ImClass c, Appendable sb, int indent) { + append(sb, "class "); + append(sb, c.getName()); + append(sb, smallHash(c)); + printTypeVariables(c.getTypeVariables(), sb, indent); + append(sb, " extends "); + for (ImClassType sc : c.getSuperClasses()) { + sc.print(sb, indent); + append(sb, " "); } + append(sb, "{\n"); + for (ImVar f : c.getFields()) { + indent(sb, indent + 1); + f.print(sb, indent + 1); + append(sb, "\n"); + } + append(sb, "\n\n"); + for (ImMethod m : c.getMethods()) { + indent(sb, indent + 1); + printMethod(sb, m, indent + 1); + } + + for (ImFunction func : c.getFunctions()) { + func.print(sb, indent + 1); + } + + append(sb, "}\n\n"); + } + private static void printMethod(Appendable sb, ImMethod m, int indent) { + if (m.getIsAbstract()) { + append(sb, "abstract "); + } + append(sb, "method "); + m.getMethodClass().print(sb, indent); + append(sb, "."); + append(sb, m.getName()); + append(sb, smallHash(m)); + append(sb, " implemented by "); + append(sb, m.getImplementation().getName()); + append(sb, smallHash(m.getImplementation())); + append(sb, "\n"); + for (ImMethod sm : m.getSubMethods()) { + append(sb, " sub: "); + sm.getMethodClass().print(sb, indent); + append(sb, "."); + append(sb, sm.getName()); + append(sb, smallHash(sm)); + append(sb, "\n"); + } + append(sb, "\n"); } @@ -78,19 +101,20 @@ public static void print(ImSimpleType p, Appendable sb, int indent) { } public static void print(ImArrayType t, Appendable sb, int indent) { - append(sb, "array "); + append(sb, "array<"); t.getEntryType().print(sb, indent); + append(sb, ">"); } public static void print(ImTupleType p, Appendable sb, int indent) { - append(sb, "<"); + append(sb, "⦅"); boolean first = true; for (ImType t : p.getTypes()) { if (!first) append(sb, ", "); t.print(sb, indent); first = false; } - append(sb, ">"); + append(sb, "⦆"); } public static void print(ImVoid p, Appendable sb, int indent) { @@ -98,12 +122,15 @@ public static void print(ImVoid p, Appendable sb, int indent) { } public static void print(ImFunction p, Appendable sb, int indent) { + indent(sb, indent); for (FunctionFlag flag : p.getFlags()) { append(sb, flag); append(sb, " "); } append(sb, "function "); append(sb, p.getName()); + append(sb, smallHash(p)); + printTypeVariables(p.getTypeVariables(), sb, indent); append(sb, "("); boolean first = true; for (ImVar p1 : p.getParameters()) { @@ -111,9 +138,12 @@ public static void print(ImFunction p, Appendable sb, int indent) { p1.print(sb, indent); first = false; } - append(sb, ") returns "); - p.getReturnType().print(sb, indent); - append(sb, "{ \n"); + append(sb, ")"); + if (!(p.getReturnType() instanceof ImVoid)) { + append(sb, " returns "); + p.getReturnType().print(sb, indent); + } + append(sb, " { \n"); for (ImVar v : p.getLocals()) { indent(sb, indent + 1); append(sb, "local "); @@ -121,9 +151,24 @@ public static void print(ImFunction p, Appendable sb, int indent) { append(sb, "\n"); } p.getBody().print(sb, indent + 1); + indent(sb, indent); append(sb, "}\n\n"); } + private static void printTypeVariables(ImTypeVars tvs, Appendable sb, int indent) { + if (tvs.isEmpty()) { + return; + } + append(sb, "<"); + for (int i = 0; i < tvs.size(); i++) { + if (i > 0) { + append(sb, ", "); + } + tvs.get(i).print(sb, indent); + } + append(sb, ">"); + } + public static void print(ImIf p, Appendable sb, int indent) { append(sb, "if "); p.getCondition().print(sb, indent); @@ -183,6 +228,8 @@ public static void print(ImStatementExpr p, Appendable sb, int indent) { public static void print(ImFunctionCall p, Appendable sb, int indent) { append(sb, p.getFunc().getName()); + append(sb, smallHash(p.getFunc())); + printTypeArguments(p.getTypeArguments(), indent, sb); printArgumentList(sb, indent, p.getArguments()); } @@ -209,7 +256,7 @@ public static void print(ImVarAccess p, Appendable sb, int indent) { } - private static String smallHash(Object g) { + public static String smallHash(Object g) { String c = "" + g.hashCode(); return c.substring(0, Math.min(3, c.length() - 1)); } @@ -275,6 +322,11 @@ public static void print(ImFuncRef p, Appendable sb, int indent) { public static void print(ImNull p, Appendable sb, int indent) { append(sb, "null"); + if (!(p.getType() instanceof ImVoid)) { + append(sb, "<"); + p.getType().print(sb, indent); + append(sb, ">"); + } } @@ -315,6 +367,10 @@ public static void print(ImOperatorCall e, Appendable sb, int indent) { } + public static String printToString(ImPrintable p) { + return asString(p); + } + public static String asString(ImPrintable p) { Appendable sb = new StringBuilder(); try { @@ -334,29 +390,42 @@ public static void print(ImMethodCall mc, Appendable sb, mc.getReceiver().print(sb, 0); append(sb, "."); append(sb, mc.getMethod().getName()); + append(sb, smallHash(mc.getMethod())); + printTypeArguments(mc.getTypeArguments(), indent, sb); printArgumentList(sb, 0, mc.getArguments()); } + public static String asString(ImFunction f) { + return f.getName() + smallHash(f); + + } public static void print(ImMemberAccess e, Appendable sb, int indent) { e.getReceiver().print(sb, 0); append(sb, "."); append(sb, e.getVar().getName()); + append(sb, smallHash(e.getVar())); + for (ImExpr index : e.getIndexes()) { + append(sb, "["); + index.print(sb, indent); + append(sb, "]"); + } + printTypeArguments(e.getTypeArguments(), indent, sb); } public static void print(ImAlloc e, Appendable sb, int indent) { - append(sb, "#alloc(class "); - append(sb, e.getClazz().getName()); + append(sb, "#alloc("); + e.getClazz().print(sb, indent); append(sb, ")"); } public static void print(ImDealloc e, Appendable sb, int indent) { - append(sb, "#dealloc(class "); - append(sb, e.getClazz().getName()); + append(sb, "#dealloc("); + e.getClazz().print(sb, indent); append(sb, ", "); e.getObj().print(sb, 0); append(sb, ")"); @@ -367,9 +436,7 @@ public static void print(ImInstanceof e, Appendable sb, int indent) { e.getObj().print(sb, 0); append(sb, " instanceof "); - append(sb, e.getClazz().getName()); - - + e.getClazz().print(sb, indent); } @@ -390,15 +457,15 @@ public static void print(ImTypeIdOfObj e, Appendable sb, public static void print(ImArrayTypeMulti imArrayTypeMulti, Appendable sb, int indent) { - append(sb, "array "); + append(sb, "array<"); imArrayTypeMulti.getEntryType().print(sb, indent); append(sb, " size: "); append(sb, imArrayTypeMulti.getArraySize()); + append(sb, ">"); } - public static void print(ImGetStackTrace e, Appendable sb, int indent) { append(sb, "#getStackTrace()"); } @@ -420,4 +487,77 @@ 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())); + } + + public static void print(ImClassType ct, Appendable sb, int indent) { + append(sb, ct.getClassDef().getName()); + append(sb, smallHash(ct.getClassDef())); + ImTypeArguments typeArguments = ct.getTypeArguments(); + printTypeArguments(typeArguments, indent, sb); + } + + private static void printTypeArguments(ImTypeArguments typeArguments, int indent, Appendable sb) { + if (!typeArguments.isEmpty()) { + append(sb, "<"); + boolean first = true; + for (ImTypeArgument ta : typeArguments) { + if (!first) { + append(sb, ", "); + } + ta.getType().print(sb, indent); + first = false; + } + append(sb, ">"); + } + } + + public static void print(ImTypeVar tv, Appendable sb, int indent) { + append(sb, tv.getName()); + append(sb, smallHash(tv)); + } + + public static String asString(ImStmts s) { + return asString((ImPrintable) s); + } + + public static String asString(List s) { + return "[" + ((List) s).stream() + .map(Object::toString) + .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); + } + + public static String asString(ImMethod s) { + return s.getName() + smallHash(s); + } + + public static String asString(ImTypeArgument s) { + return s.getType() + "" + s.getTypeClassBinding(); + } + + public static void print(ImCast e, Appendable sb, int indent) { + append(sb, "("); + e.getExpr().print(sb, indent); + append(sb, " castTo "); + e.getToType().print(sb, indent); + append(sb, ")"); + } } 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 b19e5c192..0c8bb75d2 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 @@ -17,6 +17,7 @@ import de.peeeq.wurstscript.jassIm.ImArrayType; import de.peeeq.wurstscript.jassIm.ImArrayTypeMulti; import de.peeeq.wurstscript.jassIm.ImClass; +import de.peeeq.wurstscript.jassIm.ImClassType; import de.peeeq.wurstscript.jassIm.ImExprs; import de.peeeq.wurstscript.jassIm.ImFuncRef; import de.peeeq.wurstscript.jassIm.ImFunction; @@ -25,11 +26,12 @@ import de.peeeq.wurstscript.jassIm.ImProg; import de.peeeq.wurstscript.jassIm.ImReturn; import de.peeeq.wurstscript.jassIm.ImSimpleType; -import de.peeeq.wurstscript.jassIm.ImStatementExpr; import de.peeeq.wurstscript.jassIm.ImStmts; -import de.peeeq.wurstscript.jassIm.ImTupleExpr; -import de.peeeq.wurstscript.jassIm.ImTupleSelection; import de.peeeq.wurstscript.jassIm.ImTupleType; +import de.peeeq.wurstscript.jassIm.ImTypeArguments; +import de.peeeq.wurstscript.jassIm.ImTypeVar; +import de.peeeq.wurstscript.jassIm.ImTypeVarRef; +import de.peeeq.wurstscript.jassIm.ImTypeVars; import de.peeeq.wurstscript.jassIm.ImVar; import de.peeeq.wurstscript.jassIm.ImVars; import de.peeeq.wurstscript.jassIm.ImVoid; @@ -89,19 +91,20 @@ public class ImTranslator { private boolean isUnitTestMode; - private ImVar lastInitFunc = JassIm.ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(), "lastInitFunc", false); + private ImVar lastInitFunc = JassIm.ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(this), "lastInitFunc", false); private int compiletimeOrderCounter = 1; private final Map compiletimeFlags = new HashMap<>(); private final Map compiletimeExpressionsOrder = new HashMap<>(); de.peeeq.wurstscript.ast.Element lasttranslatedThing; + private boolean debug = false; public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode) { this.wurstProg = wurstProg; this.lasttranslatedThing = wurstProg; this.isUnitTestMode = isUnitTestMode; - imProg = ImProg(wurstProg, ImVars(), ImFunctions(), JassIm.ImClasses(), new LinkedHashMap<>()); + imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new LinkedHashMap<>()); } @@ -110,9 +113,9 @@ public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode) { */ public ImProg translateProg() { try { - globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(getGlobalInitFunc()); - debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(), "msg", + debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg", false)), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ)); calculateCompiletimeOrder(); @@ -122,11 +125,11 @@ public ImProg translateProg() { } if (mainFunc == null) { - mainFunc = ImFunction(emptyTrace, "main", ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(mainFunc); } if (configFunc == null) { - configFunc = ImFunction(emptyTrace, "config", ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); + configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()); addFunction(configFunc); } finishInitFunctions(); @@ -251,7 +254,7 @@ private void removeDuplicateNatives(ImProg imProg) { if (f.isNative() && natives.containsKey(f.getName())) { ImFunction existing = natives.get(f.getName()); if (!compatibleTypes(f, existing)) { - throw new CompileError(f.attrTrace().attrErrorPos(), "Native function definition conflicts with other native function defined in " + + throw new CompileError(f, "Native function definition conflicts with other native function defined in " + existing.attrTrace().attrErrorPos()); } // remove duplicate @@ -320,7 +323,7 @@ private void translateCompilationUnit(CompilationUnit cu) { private void finishInitFunctions() { // init globals, at beginning of main func: - getMainFunc().getBody().add(0, ImFunctionCall(emptyTrace, globalInitFunc, ImExprs(), false, CallType.NORMAL)); + getMainFunc().getBody().add(0, ImFunctionCall(emptyTrace, globalInitFunc, ImTypeArguments(), ImExprs(), false, CallType.NORMAL)); for (ImFunction initFunc : initFuncMap.values()) { @@ -336,7 +339,7 @@ private void finishInitFunctions() { ImFunction native_DestroyTrigger = getNativeFunc("DestroyTrigger"); if (native_DestroyTrigger != null) { - getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, + getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); } } @@ -349,7 +352,7 @@ private ImVar prepareTrigger() { // initTrigVar = CreateTrigger() ImFunction createTrigger = getNativeFunc("CreateTrigger"); if (createTrigger != null) { - getMainFunc().getBody().add(ImSet(getMainFunc().getTrace(), ImVarAccess(initTrigVar), JassIm.ImFunctionCall(getMainFunc().getTrace(), getNativeFunc("CreateTrigger"), JassIm.ImExprs(), false, CallType.NORMAL))); + getMainFunc().getBody().add(ImSet(getMainFunc().getTrace(), ImVarAccess(initTrigVar), JassIm.ImFunctionCall(getMainFunc().getTrace(), getNativeFunc("CreateTrigger"), ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL))); } return initTrigVar; } @@ -383,7 +386,7 @@ private void callInitFunc(Set calledInitializers, WPackage p, ImVar in boolean successful = createInitFuncCall(p, initTrigVar, initFunc); if (!successful) { - getMainFunc().getBody().add(ImFunctionCall(initFunc.getTrace(), initFunc, ImExprs(), false, CallType.NORMAL)); + getMainFunc().getBody().add(ImFunctionCall(initFunc.getTrace(), initFunc, ImTypeArguments(), ImExprs(), false, CallType.NORMAL)); } } @@ -410,7 +413,7 @@ private boolean createInitFuncCall(WPackage p, ImVar initTrigVar, ImFunction ini // rewrite init func to return boolean true: - initFunc.setReturnType(WurstTypeBool.instance().imTranslateType()); + initFunc.setReturnType(WurstTypeBool.instance().imTranslateType(this)); initFunc.accept(new ImFunction.DefaultVisitor() { @Override public void visit(ImReturn imReturn) { @@ -423,21 +426,20 @@ public void visit(ImReturn imReturn) { // TriggerAddCondition(initTrigVar, Condition(function myInit)) - mainBody.add(JassIm.ImFunctionCall(trace, native_TriggerAddCondition, JassIm.ImExprs( + mainBody.add(ImFunctionCall(trace, native_TriggerAddCondition, ImTypeArguments(), JassIm.ImExprs( JassIm.ImVarAccess(initTrigVar), - JassIm.ImFunctionCall(trace, native_Condition, JassIm.ImExprs( - JassIm.ImFuncRef(initFunc)), false, CallType.NORMAL) + 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( - JassIm.ImFunctionCall(trace, native_TriggerEvaluate, - JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL) + 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( - JassIm.ImFunctionCall(trace, native_DisplayTimedTextToPlayer, JassIm.ImExprs( - JassIm.ImFunctionCall(trace, native_GetLocalPlayer, JassIm.ImExprs(), false, CallType.NORMAL), + ImFunctionCall(trace, native_DisplayTimedTextToPlayer, ImTypeArguments(), JassIm.ImExprs( + ImFunctionCall(trace, native_GetLocalPlayer, ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL), JassIm.ImRealVal("0."), JassIm.ImRealVal("0."), JassIm.ImRealVal("45."), @@ -446,11 +448,24 @@ public void visit(ImReturn imReturn) { ), // else: JassIm.ImStmts())); - mainBody.add(JassIm.ImFunctionCall(trace, native_ClearTrigger, - JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); + mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)); return true; } + private void addFunction(ImFunction f, StructureDef s) { + ImClass c = getClassFor(s.attrNearestClassOrInterface()); + c.getFunctions().add(f); + } + + private void addFunction(ImFunction f, TranslatedToImFunction funcDef) { + ImClass classForFunc = getClassForFunc(funcDef); + if (classForFunc != null) { + classForFunc.getFunctions().add(f); + } else { + addFunction(f); + } + } + private void addFunction(ImFunction f) { imProg.getFunctions().add(f); } @@ -521,10 +536,10 @@ public ImExpr getDefaultValueForJassType(ImType type) { @Override public ImFunction initFor(StructureDef classDef) { - ImVars params = ImVars(JassIm.ImVar(classDef, TypesHelper.imInt(), "this", false)); - ImFunction f = JassIm.ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), - flags()); - addFunction(f); + ImVars params = ImVars(JassIm.ImVar(classDef, selfType(classDef), "this", false)); + + ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags()); + addFunction(f, classDef); return f; } }; @@ -534,18 +549,79 @@ public ImFunction initFor(StructureDef classDef) { @Override public ImMethod initFor(StructureDef classDef) { ImFunction impl = destroyFunc.getFor(classDef); - ImMethod m = JassIm.ImMethod(classDef, "destroy" + classDef.getName(), + ImMethod m = JassIm.ImMethod(classDef, selfType(classDef), "destroy" + classDef.getName(), impl, Lists.newArrayList(), false); return m; } }; + private ImType selfType(TranslatedToImFunction f) { + return f.match(new TranslatedToImFunction.Matcher() { + @Override + public ImType case_FuncDef(FuncDef f) { + return selfType(f); + } + + @Override + public ImType case_ConstructorDef(ConstructorDef f) { + return selfType(f.attrNearestClassOrInterface()); + } + + @Override + public ImType case_NativeFunc(NativeFunc f) { + throw new CompileError(f, "Cannot use 'this' here."); + } + + @Override + public ImType case_OnDestroyDef(OnDestroyDef f) { + return selfType(f.attrNearestClassOrInterface()); + } + + @Override + public ImType case_TupleDef(TupleDef f) { + throw new CompileError(f, "Cannot use 'this' here."); + } + + @Override + public ImType case_ExprClosure(ExprClosure f) { + return selfType(getClassForClosure(f)); + } + + @Override + public ImType case_InitBlock(InitBlock f) { + throw new CompileError(f, "Cannot use 'this' here."); + } + + @Override + public ImType case_ExtensionFuncDef(ExtensionFuncDef f) { + return f.getExtendedType().attrTyp().imTranslateType(ImTranslator.this); + } + }); + } + + private ImClassType selfType(FuncDef f) { + return selfType(f.attrNearestClassOrInterface()); + } + + public ImClassType selfType(StructureDef classDef) { + ImClass imClass = getClassFor(classDef.attrNearestClassOrInterface()); + 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())); + } + return JassIm.ImClassType(imClass, typeArgs); + } + public GetAForB allocFunc = new GetAForB() { @Override public ImFunction initFor(ImClass c) { - return JassIm.ImFunction(c.getTrace(), "alloc_" + c.getName(), JassIm.ImVars(), TypesHelper.imInt(), - JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); + + return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); } }; @@ -554,13 +630,24 @@ public ImFunction initFor(ImClass c) { @Override public ImFunction initFor(ImClass c) { - return JassIm.ImFunction(c.getTrace(), "dealloc_" + c.getName(), 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(JassIm.ImVar(c.getTrace(), TypesHelper.imInt(), "obj", false)), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList()); } }; + private final Map typeVariableReverse = new HashMap<>(); + + private final GetAForB typeVariable = new GetAForB() { + + @Override + public ImTypeVar initFor(TypeParamDef a) { + ImTypeVar v = JassIm.ImTypeVar(a.getName()); + typeVariableReverse.put(v, a); + return v; + } + }; + public ImFunction getFuncFor(TranslatedToImFunction funcDef) { if (functionMap.containsKey(funcDef)) { @@ -613,14 +700,120 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) { } } - ImFunction f = JassIm.ImFunction(funcDef, name, ImVars(), ImVoid(), ImVars(), ImStmts(), flags); + ImTypeVars typeVars = collectTypeVarsForFunction(funcDef); + ImFunction f = ImFunction(funcDef, name, typeVars, ImVars(), ImVoid(), ImVars(), ImStmts(), flags); funcDef.imCreateFuncSkeleton(this, f); - addFunction(f); + addFunction(f, funcDef); functionMap.put(funcDef, f); return f; } + private ImClass getClassForFunc(TranslatedToImFunction funcDef) { + if (funcDef == null) { + return null; + } + return funcDef.match(new TranslatedToImFunction.Matcher() { + @Override + public ImClass case_TupleDef(TupleDef tupleDef) { + return null; + } + + @Override + public ImClass case_FuncDef(FuncDef funcDef) { + if (funcDef.attrIsDynamicClassMember()) { + return getClassFor(funcDef.attrNearestClassOrInterface()); + } + return null; + } + + @Override + public ImClass case_NativeFunc(NativeFunc nativeFunc) { + return null; + } + + @Override + public ImClass case_OnDestroyDef(OnDestroyDef funcDef) { + return getClassFor(funcDef.attrNearestClassOrInterface()); + } + + @Override + public ImClass case_InitBlock(InitBlock initBlock) { + return null; + } + + @Override + public ImClass case_ExtensionFuncDef(ExtensionFuncDef extensionFuncDef) { + return null; + } + + @Override + public ImClass case_ConstructorDef(ConstructorDef funcDef) { + return getClassFor(funcDef.attrNearestClassOrInterface()); + } + + @Override + public ImClass case_ExprClosure(ExprClosure exprClosure) { + return null; + } + }); + } + + private ImTypeVars collectTypeVarsForFunction(TranslatedToImFunction funcDef) { + ImTypeVars typeVars = ImTypeVars(); + funcDef.match(new TranslatedToImFunction.MatcherVoid() { + @Override + public void case_FuncDef(FuncDef funcDef) { + handleTypeParameters(funcDef.getTypeParameters()); + } + + + private void handleTypeParameters(TypeParamDefs tps) { + for (TypeParamDef tp : tps) { + handleTypeParameter(tp); + } + } + + private void handleTypeParameter(TypeParamDef tp) { + if (tp.getTypeParamConstraints() instanceof TypeExprList) { + typeVars.add(typeVariable.getFor(tp)); + } + } + + @Override + public void case_ConstructorDef(ConstructorDef constructorDef) { + } + + @Override + public void case_NativeFunc(NativeFunc nativeFunc) { + } + + @Override + public void case_OnDestroyDef(OnDestroyDef onDestroyDef) { + } + + @Override + public void case_TupleDef(TupleDef tupleDef) { + } + + @Override + public void case_ExprClosure(ExprClosure exprClosure) { + // TODO where to set closure parameters? + } + + @Override + public void case_InitBlock(InitBlock initBlock) { + + } + + @Override + public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) { + handleTypeParameters(funcDef.getTypeParameters()); + } + }); + return typeVars; + } + private boolean isExtern(TranslatedToImFunction funcDef) { if (funcDef instanceof HasModifier) { @@ -645,7 +838,7 @@ private boolean isBJ(WPos source) { public ImFunction getInitFuncFor(WPackage p) { // TODO more precise trace - return initFuncMap.computeIfAbsent(p, p1 -> JassIm.ImFunction(p1, "init_" + p1.getName(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags())); + return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags())); } /** @@ -702,7 +895,7 @@ public ImVar getThisVar(TranslatedToImFunction f) { if (thisVarMap.containsKey(f)) { return thisVarMap.get(f); } - ImVar v = JassIm.ImVar(f, ImSimpleType("integer"), "this", false); + ImVar v = JassIm.ImVar(f, selfType(f), "this", false); thisVarMap.put(f, v); return v; } @@ -745,7 +938,7 @@ public int getTupleIndex(TupleDef tupleDef, VarDef parameter) { public ImVar getVarFor(VarDef varDef) { ImVar v = varMap.get(varDef); if (v == null) { - ImType type = varDef.attrTyp().imTranslateType(); + ImType type = varDef.attrTyp().imTranslateType(this); String name = varDef.getName(); if (isNamedScopeVar(varDef)) { name = getNameFor(varDef.attrNearestNamedScope()) + "_" + name; @@ -922,8 +1115,9 @@ public ImFunction getConstructFunc(ConstructorDef constr) { for (WParameter p : constr.getParameters()) { params.add(getVarFor(p)); } - f = JassIm.ImFunction(constr, name, params, ImVoid(), ImVars(), ImStmts(), flags()); - addFunction(f); + + f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), flags()); + addFunction(f, constr); constructorFuncs.put(constr, f); } return f; @@ -954,8 +1148,9 @@ public ImFunction getConstructNewFunc(ConstructorDef constr) { ImFunction f = constrNewFuncs.get(constr); if (f == null) { String name = "new_" + constr.attrNearestClassDef().getName(); - f = JassIm.ImFunction(constr, name, ImVars(), TypesHelper.imInt(), ImVars(), ImStmts(), flags()); - addFunction(f); + + f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags()); + addFunction(f, constr); constrNewFuncs.put(constr, f); } return f; @@ -1073,6 +1268,14 @@ public void setEclipseMode(boolean enabled) { isEclipseMode = enabled; } + public TypeParamDef getTypeParamDef(ImTypeVar tv) { + return typeVariableReverse.get(tv); + } + + public ImTypeVar getTypeVar(TypeParamDef tv) { + return typeVariable.getFor(tv); + } + interface VarsForTupleResult { @@ -1182,6 +1385,11 @@ public VarsForTupleResult case_ImArrayType(ImArrayType at) { return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); } + @Override + public VarsForTupleResult case_ImTypeVarRef(ImTypeVarRef imTypeVarRef) { + throw new RuntimeException("Should be called after eliminating generics."); + } + @Override public VarsForTupleResult case_ImArrayTypeMulti(ImArrayTypeMulti at) { if (at.getEntryType() instanceof ImTupleType) { @@ -1204,6 +1412,12 @@ public VarsForTupleResult case_ImVoid(ImVoid imVoid) { return new TupleResult(Collections.emptyList()); } + @Override + public VarsForTupleResult case_ImClassType(ImClassType st) { + ImType type = typeConstructor.apply(st); + return new SingleVarResult(JassIm.ImVar(tr, type, name, false)); + } + @Override public VarsForTupleResult case_ImSimpleType(ImSimpleType st) { ImType type = typeConstructor.apply(st); @@ -1269,7 +1483,10 @@ public ImType getOriginalReturnValue(ImFunction f) { } public void assertProperties(AssertProperty... properties1) { - final Set properties = Sets.newEnumSet(Lists.newArrayList(properties1), AssertProperty.class); + if (!debug) { + return; + } + final Set properties = Sets.newHashSet(properties1); assertProperties(properties, imProg); } @@ -1277,23 +1494,12 @@ public void assertProperties(Set properties, Element e) { if (e instanceof ElementWithVar) { checkVar(((ElementWithVar) e).getVar(), properties); } + properties.forEach(p -> p.check(e)); if (properties.contains(AssertProperty.NOTUPLES)) { - if (e instanceof ImTupleExpr - || e instanceof ImTupleSelection - ) { - throw new Error("contains tuple exprs " + e); - } - if (e instanceof ImVar) { - ImVar v = (ImVar) e; - if (TypesHelper.typeContainsTuples(v.getType())) { - throw new Error("contains tuple var: " + v + " in\n" + v.getParent().getParent()); - } - } + } if (properties.contains(AssertProperty.FLAT)) { - if (e instanceof ImStatementExpr) { - throw new Error("contains statementExpr " + e); - } + } for (int i = 0; i < e.size(); i++) { Element child = e.get(i); @@ -1340,12 +1546,31 @@ public boolean isUnitTestMode() { return isUnitTestMode; } + private Map classForClosure = Maps.newLinkedHashMap(); - Map classForStructureDef = Maps.newLinkedHashMap(); + public ImClass getClassForClosure(ExprClosure s) { + Preconditions.checkNotNull(s); + return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList())); + } + + + private Map classForStructureDef = Maps.newLinkedHashMap(); - public ImClass getClassFor(StructureDef s) { - return classForStructureDef.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, s1.getName(), JassIm.ImVars(), JassIm.ImMethods(), - Lists.newArrayList())); + public ImClass getClassFor(ClassOrInterface 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); + } + } + } + + return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()); + }); } @@ -1355,7 +1580,7 @@ public ImMethod getMethodFor(FuncDef f) { ImMethod m = methodForFuncDef.get(f); if (m == null) { ImFunction imFunc = getFuncFor(f); - m = JassIm.ImMethod(f, elementNameWithPath(f), imFunc, Lists.newArrayList(), false); + m = JassIm.ImMethod(f, selfType(f), elementNameWithPath(f), imFunc, Lists.newArrayList(), false); methodForFuncDef.put(f, m); } return m; @@ -1379,8 +1604,8 @@ public Map getClassManagementVars() { Partitions p = new Partitions<>(); for (ImClass c : imProg.getClasses()) { p.add(c); - for (ImClass sc : c.getSuperClasses()) { - p.union(c, sc); + for (ImClassType sc : c.getSuperClasses()) { + p.union(c, sc.getClassDef()); } } // generate typeId variables @@ -1406,7 +1631,7 @@ public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr mes ef = errorFunc = f.orElseGet(this::makeDefaultErrorFunc); } ImExprs arguments = JassIm.ImExprs(message); - return JassIm.ImFunctionCall(trace, ef, arguments, false, CallType.NORMAL); + return ImFunctionCall(trace, ef, ImTypeArguments(), arguments, false, CallType.NORMAL); } private ImFunction makeDefaultErrorFunc() { @@ -1417,9 +1642,7 @@ private ImFunction makeDefaultErrorFunc() { ImStmts body = JassIm.ImStmts(); // print message: - body.add(JassIm.ImFunctionCall(emptyTrace, getDebugPrintFunction(), - JassIm.ImExprs(JassIm.ImVarAccess(msgVar)), - false, CallType.NORMAL)); + body.add(ImFunctionCall(emptyTrace, getDebugPrintFunction(), ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(msgVar)), false, CallType.NORMAL)); // TODO divide by zero to crash thread: @@ -1433,7 +1656,8 @@ private ImFunction makeDefaultErrorFunc() { // List flags = Lists.newArrayList(); - return JassIm.ImFunction(emptyTrace, "error", parameters, returnType, locals, body, flags); + + return ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags); } 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 971ca4944..e9dfef0f6 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 @@ -2,13 +2,21 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.Lists; -import de.peeeq.wurstscript.ast.*; +import de.peeeq.wurstscript.ast.ClassDef; +import de.peeeq.wurstscript.ast.FuncDef; +import de.peeeq.wurstscript.ast.InterfaceDef; +import de.peeeq.wurstscript.ast.TypeExpr; import de.peeeq.wurstscript.jassIm.*; -import de.peeeq.wurstscript.types.*; +import de.peeeq.wurstscript.types.VariableBinding; +import de.peeeq.wurstscript.types.WurstTypeClass; +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; +import java.util.stream.Collectors; public class InterfaceTranslator { @@ -27,10 +35,7 @@ public void translate() { // set super-classes for (TypeExpr ext : interfaceDef.getExtendsList()) { - if (ext.attrTypeDef() instanceof StructureDef) { - StructureDef sup = (StructureDef) ext.attrTypeDef(); - imClass.getSuperClasses().add(translator.getClassFor(sup)); - } + imClass.getSuperClasses().add((ImClassType) ext.attrTyp().imTranslateType(translator)); } // create dispatch methods @@ -57,7 +62,14 @@ public void addDestroyMethod() { // deallocate ImFunction f = translator.destroyFunc.getFor(interfaceDef); ImVar thisVar = f.getParameters().get(0); - f.getBody().add(JassIm.ImDealloc(imClass, JassIm.ImVarAccess(thisVar))); + f.getBody().add(JassIm.ImDealloc(interfaceDef, imClassType(), JassIm.ImVarAccess(thisVar))); + } + + private ImClassType imClassType() { + ImTypeArguments typeArgs = imClass.getTypeVariables().stream() + .map(tv -> JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap())) + .collect(Collectors.toCollection(JassIm::ImTypeArguments)); + return JassIm.ImClassType(imClass, typeArgs); } private void translateInterfaceFuncDef(FuncDef f) { 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 636113d39..0392ee215 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 @@ -109,10 +109,10 @@ private void replaceVars(Element e, Map oldToNewVar) { args.add(set.getRight().copy()); if (generateStacktraces) { - args.add(JassIm.ImStringVal("when writing array " + va.getVar().getName() + " in " + StackTraceInjector2.getCallPos(va.getTrace().attrSource()))); + args.add(JassIm.ImStringVal("when writing array " + va.getVar().getName() + StackTraceInjector2.getCallPos(va.getTrace().attrSource()))); } - set.replaceBy(JassIm.ImFunctionCall(set.getTrace(), getSetMap.get(va.getVar()).setter, args, false, CallType.NORMAL)); + set.replaceBy(JassIm.ImFunctionCall(set.getTrace(), getSetMap.get(va.getVar()).setter, JassIm.ImTypeArguments(), args, false, CallType.NORMAL)); return; } } @@ -137,7 +137,7 @@ private void replaceVars(Element e, Map oldToNewVar) { args.add(JassIm.ImStringVal("when reading array " + am.getVar().getName() + " in " + StackTraceInjector2.getCallPos(am.getTrace().attrSource()))); } if (getSetMap.containsKey(am.getVar())) { - am.replaceBy(JassIm.ImFunctionCall(am.attrTrace(), getSetMap.get(am.getVar()).getter, args, false, CallType.NORMAL)); + am.replaceBy(JassIm.ImFunctionCall(am.attrTrace(), getSetMap.get(am.getVar()).getter, JassIm.ImTypeArguments(), args, false, CallType.NORMAL)); } } } @@ -160,7 +160,8 @@ private ImFunction generateSetFunc(ImVar aVar, List newArrays) { ImExpr condition = JassIm.ImOperatorCall(WurstOperator.OR, JassIm.ImExprs(lowCond, highCond)); ImStmts body = JassIm.ImStmts(JassIm.ImIf(aVar.getTrace(), condition, thenBlock, elseBlock)); - ImFunction setFunc = JassIm.ImFunction(aVar.getTrace(), aVar.getName() + "_set", JassIm.ImVars(instanceId, arrayIndex, value), JassIm.ImVoid(), locals, body, Lists.newArrayList()); + + 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); setFunc.getParameters().add(stackPos); @@ -213,7 +214,8 @@ private ImFunction generateGetFunc(ImVar aVar, List newArrays) { ImStmts body = JassIm.ImStmts(JassIm.ImIf(aVar.getTrace(), condition, thenBlock, elseBlock), JassIm.ImReturn(returnVal.getTrace(), JassIm.ImVarAccess(returnVal))); - ImFunction getFunc = JassIm.ImFunction(aVar.getTrace(), aVar.getName() + "_get", JassIm.ImVars(instanceId, arrayIndex), mtype.getEntryType(), locals, body, Lists.newArrayList()); + + 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); getFunc.getParameters().add(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 2e88f27e0..47aa4857d 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 @@ -4,14 +4,10 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.*; -import de.peeeq.wurstscript.utils.Utils; -import fj.P2; -import fj.data.TreeMap; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; public class OverrideUtils { @@ -99,28 +95,29 @@ public static void addOverride( FuncDef toIndex = argFromIndexFuncs.get(i); if (toIndex != null) { ImFunction toIndexF = tr.getFuncFor(toIndex); - arg = JassIm.ImFunctionCall(e, toIndexF, JassIm.ImExprs(arg), false, CallType.NORMAL); + arg = JassIm.ImFunctionCall(e, toIndexF, JassIm.ImTypeArguments(), JassIm.ImExprs(arg), false, CallType.NORMAL); } arguments.add(arg); } - ImExpr wrappedCall = JassIm.ImFunctionCall(e, subMethod.getImplementation(), arguments, false, CallType.NORMAL); + ImExpr wrappedCall = JassIm.ImFunctionCall(e, subMethod.getImplementation(), JassIm.ImTypeArguments(), arguments, false, CallType.NORMAL); if (rType instanceof ImVoid) { body.add(wrappedCall); } else { if (retToIndexFunc != null) { ImFunction toIndexF = tr.getFuncFor(retToIndexFunc); - wrappedCall = JassIm.ImFunctionCall(e, toIndexF, JassIm.ImExprs(wrappedCall), false, CallType.NORMAL); + wrappedCall = JassIm.ImFunctionCall(e, toIndexF, JassIm.ImTypeArguments(), JassIm.ImExprs(wrappedCall), false, CallType.NORMAL); } body.add(JassIm.ImReturn(e, wrappedCall)); } List flags = Collections.emptyList(); - ImFunction implementation = JassIm.ImFunction(e, subMethod.getName() + "_wrapper", parameters, rType, locals, body, flags); + + 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.getName() + "_wrapper", implementation, subMethods, false); + ImMethod wrapperMethod = JassIm.ImMethod(e, subMethod.getMethodClass(), subMethod.getName() + "_wrapper", implementation, subMethods, 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 e23a1c53d..fb1cc016b 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 @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.types.TypesHelper; +import de.peeeq.wurstscript.utils.Constants; /** * Manages object ids in a queue. This way the time each object is @@ -19,12 +20,12 @@ public void createAllocFunc(ImTranslator translator, ImProg prog, ImClass c) { ImStmts body = f.getBody(); Element tr = c.getTrace(); - ImVar thisVar = JassIm.ImVar(tr, TypesHelper.imInt(), "this", false); + ImVar thisVar = JassIm.ImVar(tr, translator.selfType(c), "this", false); // TODO change type locals.add(thisVar); ClassManagementVars mVars = translator.getClassManagementVarsFor(c); - int maxSize = 32768; + int maxSize = Constants.MAX_ARRAY_SIZE; // if freeCount == 0 then ImStmts elseBlock = JassIm.ImStmts(); ImStmts thenBlock = JassIm.ImStmts(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ReferenceRewritingCopy.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ReferenceRewritingCopy.java index 317312797..5bd4a6c3d 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ReferenceRewritingCopy.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ReferenceRewritingCopy.java @@ -90,7 +90,7 @@ public void visit(ImAlloc e) { super.visit(e); Element newChild = oldToNew.get(e.getClazz()); if (newChild != null) { - e.setClazz((ImClass) newChild); + e.setClazz((ImClassType) newChild); } } @@ -99,7 +99,7 @@ public void visit(ImDealloc e) { super.visit(e); Element newChild = oldToNew.get(e.getClazz()); if (newChild != null) { - e.setClazz((ImClass) newChild); + e.setClazz((ImClassType) newChild); } } @@ -117,7 +117,7 @@ public void visit(ImInstanceof e) { super.visit(e); Element newChild = oldToNew.get(e.getClazz()); if (newChild != null) { - e.setClazz((ImClass) newChild); + e.setClazz((ImClassType) newChild); } } @@ -126,7 +126,7 @@ public void visit(ImTypeIdOfObj e) { super.visit(e); Element newChild = oldToNew.get(e.getClazz()); if (newChild != null) { - e.setClazz((ImClass) newChild); + e.setClazz((ImClassType) newChild); } } @@ -135,7 +135,7 @@ public void visit(ImTypeIdOfClass e) { super.visit(e); Element newChild = oldToNew.get(e.getClazz()); if (newChild != null) { - e.setClazz((ImClass) newChild); + e.setClazz((ImClassType) newChild); } } 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 9cb23347a..c0f7b7600 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,6 +8,8 @@ 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.jassIm.*; import de.peeeq.wurstscript.parser.WPos; @@ -216,14 +218,14 @@ private void passStacktraceParams(final Multimap cal // pass the stacktrace parameter at all calls for (ImFunctionCall call : callsForF) { String callPos = getCallPos(call.attrTrace().attrErrorPos()); - call.getArguments().add(str("when calling " + name(f) + " in " + callPos)); + call.getArguments().add(str("when calling " + name(f) + "" + callPos)); } } } private String name(ImFunction f) { @Nullable NameDef nameDef = f.attrTrace().tryGetNameDef(); - if (nameDef != null) { + if (nameDef instanceof FunctionDefinition) { return nameDef.getName(); } return f.getName(); @@ -234,7 +236,7 @@ public static String getCallPos(WPos source) { if (source.getFile().startsWith("<")) { callPos = ""; } else { - callPos = source.printShort(); + callPos = " in " + source.printShort(); } return callPos; } @@ -258,8 +260,8 @@ private void rewriteFuncRefs(final List funcRefs, Set aff ImVars params = f.getParameters().copy(); // remove stacktrace param params.remove(params.size() - 1); - ImFunction bridgeFunc = JassIm.ImFunction(f.getTrace(), "bridge_" + f.getName(), params, - f.getReturnType().copy(), JassIm.ImVars(), JassIm.ImStmts(), f.getFlags()); + + ImFunction bridgeFunc = JassIm.ImFunction(f.getTrace(), "bridge_" + f.getName(), JassIm.ImTypeVars(), params, f.getReturnType().copy(), JassIm.ImVars(), JassIm.ImStmts(), f.getFlags()); prog.getFunctions().add(bridgeFunc); ImStmt stmt; @@ -268,12 +270,12 @@ private void rewriteFuncRefs(final List funcRefs, Set aff ImStmts body = bridgeFunc.getBody(); de.peeeq.wurstscript.ast.Element trace = fr.attrTrace(); if (trace.getParent() == null) { - throw new RuntimeException("no trace"); + throw new RuntimeException("func ref " + fr + " has no trace: " + trace); } // reset stack and add information for callback: body.add(JassIm.ImSet(trace, JassIm.ImVarAccess(stackSize), JassIm.ImIntVal(0))); - ImFunctionCall call = JassIm.ImFunctionCall(fr.attrTrace(), f, args, true, CallType.NORMAL); + ImFunctionCall call = JassIm.ImFunctionCall(fr.attrTrace(), f, JassIm.ImTypeArguments(), args, true, CallType.NORMAL); if (bridgeFunc.getReturnType() instanceof ImVoid) { stmt = call; } else { 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 6e4052cc2..51cec8c50 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 @@ -92,11 +92,10 @@ 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(JassIm.ImFunctionCall(s, hasNextFuncIm, fromTarget, false, CallType + imBody.add(ImExitwhen(s, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(ImFunctionCall(s, hasNextFuncIm, ImTypeArguments(), fromTarget, false, CallType .NORMAL))))); // elem = next() - ImFunctionCall nextCall = JassIm.ImFunctionCall(s, nextFuncIm, fromTarget.copy(), - false, CallType.NORMAL); + ImFunctionCall nextCall = ImFunctionCall(s, nextFuncIm, ImTypeArguments(), fromTarget.copy(), false, CallType.NORMAL); WurstType nextReturn = nextFunc.getReturnType(); ImExpr nextCallWrapped = ExprTranslation.wrapTranslation(s, t, nextCall, nextReturn, loopVarType); @@ -146,7 +145,7 @@ public static ImStmt translate(StmtForIn forIn, ImTranslator t, ImFunction f) { } // call XX.iterator() - ImFunctionCall iteratorCall = JassIm.ImFunctionCall(forIn, iteratorFuncIm, iterationTargetList, false, CallType.NORMAL); + ImFunctionCall iteratorCall = ImFunctionCall(forIn, iteratorFuncIm, ImTypeArguments(), iterationTargetList, false, CallType.NORMAL); // create IM-variable for iterator ImVar iteratorVar = JassIm.ImVar(forIn.getLoopVar(), iteratorCall.attrTyp(), "iterator", false); @@ -160,12 +159,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(JassIm.ImFunctionCall(forIn, hasNextFuncIm, JassIm.ImExprs + imBody.add(ImExitwhen(forIn, JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(ImFunctionCall(forIn, hasNextFuncIm, ImTypeArguments(), JassIm.ImExprs (JassIm .ImVarAccess(iteratorVar)), false, CallType.NORMAL))))); // elem = next() - ImFunctionCall nextCall = JassIm.ImFunctionCall(forIn, nextFuncIm, JassIm.ImExprs(JassIm.ImVarAccess(iteratorVar)), false, - CallType.NORMAL); + ImFunctionCall nextCall = ImFunctionCall(forIn, nextFuncIm, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(iteratorVar)), false, CallType.NORMAL); WurstType nextReturn = nextFunc.getReturnType(); ImExpr nextCallWrapped = ExprTranslation.wrapTranslation(forIn, t, nextCall, nextReturn, loopVarType); @@ -181,7 +179,7 @@ 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(JassIm.ImFunctionCall(forIn, t.getFuncFor(funcLink.getDef()), JassIm + imReturn.replaceBy(ImHelper.statementExprVoid(JassIm.ImStmts(ImFunctionCall(forIn, t.getFuncFor(funcLink.getDef()), ImTypeArguments(), JassIm .ImExprs(JassIm.ImVarAccess(iteratorVar)), false, CallType.NORMAL), imReturn.copy()))); } @@ -191,7 +189,7 @@ public void visit(ImReturn imReturn) { result.add(ImLoop(forIn, imBody)); // close iterator after loop - closeFunc.ifPresent(nameLink -> result.add(JassIm.ImFunctionCall(forIn, t.getFuncFor(nameLink.getDef()), JassIm.ImExprs(JassIm + closeFunc.ifPresent(nameLink -> result.add(ImFunctionCall(forIn, t.getFuncFor(nameLink.getDef()), ImTypeArguments(), JassIm.ImExprs(JassIm .ImVarAccess(iteratorVar)), false, CallType.NORMAL))); } @@ -299,7 +297,7 @@ public static ImStmt translate(StmtSkip s, ImTranslator translator, ImFunction f public static ImStmt translate(SwitchStmt switchStmt, ImTranslator t, ImFunction f) { List result = Lists.newArrayList(); - ImType type = switchStmt.getExpr().attrTyp().imTranslateType(); + ImType type = switchStmt.getExpr().attrTyp().imTranslateType(t); ImExpr tempVar = addCacheVariableSmart(t, f, result, switchStmt.getExpr(), type); // generate ifs // leerer Block: diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Subclasses.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Subclasses.java index 8e948f515..36f003282 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Subclasses.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/Subclasses.java @@ -5,6 +5,7 @@ import com.google.common.collect.Multimap; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.ImClass; +import de.peeeq.wurstscript.jassIm.ImClassType; import de.peeeq.wurstscript.jassIm.ImProg; import de.peeeq.wurstscript.utils.Utils; @@ -16,7 +17,8 @@ public static Multimap calculate(ImProg prog) { Multimap result = ArrayListMultimap.create(); for (ImClass c : prog.getClasses()) { - for (ImClass sc : c.getSuperClasses()) { + for (ImClassType sct : c.getSuperClasses()) { + ImClass sc = sct.getClassDef(); if (sc == c) { throw new CompileError(c.attrTrace().attrSource(), Utils.printElement(c.attrTrace()) + " depends on itself."); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeId.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeId.java index 1258cf6b6..41db7ce73 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeId.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/TypeId.java @@ -6,7 +6,9 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.PackageOrGlobal; import de.peeeq.wurstscript.ast.WPackage; +import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.jassIm.ImClass; +import de.peeeq.wurstscript.jassIm.ImClassType; import de.peeeq.wurstscript.jassIm.ImProg; import org.eclipse.jdt.annotation.Nullable; @@ -23,6 +25,11 @@ public class TypeId { Comparator.comparing(ImClass::getName) .thenComparing(TypeId::packageName); + // sort class types by name to get deterministic order + private static final Comparator classtype_comparator = + Comparator.comparing((ImClassType ct) -> ct.getClassDef().getName()) + .thenComparing(ct -> TypeId.packageName(ct.getClassDef())); + public static Map calculate(ImProg prog) { AtomicInteger count = new AtomicInteger(); Map result = Maps.newLinkedHashMap(); @@ -58,7 +65,9 @@ private static void assignIds(AtomicInteger count, Map result, private static Multimap calculateSubclasses(List classes) { Multimap subClasses = LinkedHashMultimap.create(); for (ImClass c : classes) { - c.getSuperClasses().stream().sorted(class_comparator).forEach(superClass -> + c.getSuperClasses().stream() + .map(ImClassType::getClassDef) + .sorted(class_comparator).forEach(superClass -> subClasses.put(superClass, c) ); } @@ -80,15 +89,19 @@ private static void assignId(AtomicInteger count, Map result, } public static int get(ImClass c) { - return c.attrProg().attrTypeId().get(c); + Integer res = c.attrProg().attrTypeId().get(c); + if (res == null) { + throw new CompileError(c, "Could not get type-id for " + c.getName() + ImPrinter.smallHash(c)); + } + return res; } public static boolean isSubclass(ImClass c, ImClass other) { if (c == other) { return true; } - for (ImClass sc : c.getSuperClasses()) { - if (sc.isSubclassOf(other)) { + for (ImClassType sc : c.getSuperClasses()) { + if (sc.getClassDef().isSubclassOf(other)) { return true; } } 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 27d2285e1..8458d5420 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 @@ -142,8 +142,7 @@ public void visit(ImVarAccess va) { private void redirectCall(ImFunctionCall call, ImFunction newFunc) { // Redirect call to new function - ImFunctionCall newCall = JassIm.ImFunctionCall(call.getTrace(), newFunc, JassIm.ImExprs(call.getArguments().removeAll()), call.getTuplesEliminated(), - call.getCallType()); + ImFunctionCall newCall = JassIm.ImFunctionCall(call.getTrace(), newFunc, JassIm.ImTypeArguments(), JassIm.ImExprs(call.getArguments().removeAll()), call.getTuplesEliminated(), call.getCallType()); call.replaceBy(newCall); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java index 5a536c510..7805c4b20 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java @@ -105,7 +105,9 @@ public static List getParamNames(WParameters parameters) { public static FunctionSignature fromNameLink(FuncLink f) { - return new FunctionSignature(f.getDef(), VariableBinding.emptyMapping().withTypeVariables(fj.data.List.iterableList(f.getTypeParams())), f.getReceiverType(), f.getName(), f.getParameterTypes(), getParamNames(f.getDef().getParameters()), f.getReturnType()); + VariableBinding mapping = f.getVariableBinding(); + mapping = mapping.withTypeVariables(fj.data.List.iterableList(f.getTypeParams())); + return new FunctionSignature(f.getDef(), mapping, f.getReceiverType(), f.getName(), f.getParameterTypes(), getParamNames(f.getDef().getParameters()), f.getReturnType()); } @@ -295,11 +297,11 @@ public ArgsMatchResult tryMatchAgainstArgs(List argTypes, List int badness = 0; if (!isValidParameterNumber(argTypes.size())) { if (argTypes.size() > getMaxNumParams()) { - errors.add(new CompileError(location.attrErrorPos(), "Too many arguments: " + argTypes.size() + " given, but only " + getMaxNumParams() + + errors.add(new CompileError(location, "Too many arguments: " + argTypes.size() + " given, but only " + getMaxNumParams() + " expected.")); badness += argTypes.size() - getMaxNumParams(); } else if (argTypes.size() < getMinNumParams()) { - errors.add(new CompileError(location.attrErrorPos(), "Not enough arguments: " + argTypes.size() + " given, but " + getMinNumParams() + " expected.")); + errors.add(new CompileError(location, "Not enough arguments: " + argTypes.size() + " given, but " + getMinNumParams() + " expected.")); badness += getMinNumParams() - argTypes.size(); } } @@ -319,7 +321,7 @@ public ArgsMatchResult tryMatchAgainstArgs(List argTypes, List } if (mapping.hasUnboundTypeVars()) { - errors.add(new CompileError(location.attrErrorPos(), "Could not infer type for type variables " + mapping.printUnboundTypeVars())); + errors.add(new CompileError(location, "Could not infer type for type variables " + mapping.printUnboundTypeVars())); } errors.addAll(mapping.getErrors()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypesHelper.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypesHelper.java index 5ba25dbb3..f99b49982 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypesHelper.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/TypesHelper.java @@ -44,6 +44,8 @@ public static boolean typeContainsTuples(ImType vt) { || vt instanceof ImArrayTypeMulti && typeContainsTuples(((ImArrayTypeMulti) vt).getEntryType()); } + + // public static boolean checkTypeArgs(InstanceDef iDef, List classParams, List interfaceParams) { // if (classParams.size() == 0 && interfaceParams.size() == 0) { // return true; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstNativeType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstNativeType.java index 0a3b9f996..1745742b0 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstNativeType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstNativeType.java @@ -4,6 +4,7 @@ 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 org.eclipse.jdt.annotation.Nullable; public class WurstNativeType extends WurstType { @@ -43,6 +44,10 @@ public static WurstNativeType instance(String name, WurstType superType) { } @Override + public ImType imTranslateType(ImTranslator tr) { + return JassIm.ImSimpleType(name); + } + public ImType imTranslateType() { return JassIm.ImSimpleType(name); } @@ -52,4 +57,9 @@ public ImExprOpt getDefaultValue() { return JassIm.ImNull(imTranslateType()); } + @Override + protected boolean isNullable() { + return true; + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstType.java index 68c4c8002..a60a321f0 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstType.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import fj.data.Option; import org.eclipse.jdt.annotation.Nullable; @@ -34,7 +35,7 @@ public final boolean isSubtypeOf(WurstType other, @Nullable Element location) { *

* Will try to instantiate type variables from the set typeParams */ - public final VariableBinding matchAgainstSupertype(WurstType other, @Nullable Element location, VariableBinding mapping, VariablePosition variablePosition) { + public final @Nullable VariableBinding matchAgainstSupertype(WurstType other, @Nullable Element location, VariableBinding mapping, VariablePosition variablePosition) { if (other instanceof WurstTypeUnknown || this instanceof WurstTypeUnknown) { // everything is a subtype of unknown (stops error cascades) return mapping; @@ -176,7 +177,7 @@ public VariableBinding getTypeArgBinding() { } - public abstract ImType imTranslateType(); + public abstract ImType imTranslateType(ImTranslator tr); public abstract ImExprOpt getDefaultValue(); @@ -250,4 +251,5 @@ public boolean isNestedInside(WurstType other) { } + protected abstract boolean isNullable(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeArray.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeArray.java index 54fae9734..5c3e3df72 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeArray.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeArray.java @@ -7,6 +7,7 @@ import de.peeeq.wurstscript.intermediatelang.ILconst; import de.peeeq.wurstscript.intermediatelang.ILconstInt; import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -106,9 +107,9 @@ public int getSize(int i) { @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { initSizes(); - ImType bt = baseType.imTranslateType(); + ImType bt = baseType.imTranslateType(tr); if (sizes.length > 0) { if (sizes[0] == 0) { return JassIm.ImArrayType(bt); @@ -129,4 +130,9 @@ public ImExprOpt getDefaultValue() { throw new Error(); } + @Override + protected boolean isNullable() { + return false; + } + } 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 1f6562216..cad952024 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,16 +1,17 @@ package de.peeeq.wurstscript.types; +import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.ast.Element; -import de.peeeq.wurstscript.ast.FuncDef; -import de.peeeq.wurstscript.ast.TypeParamDef; import de.peeeq.wurstscript.attributes.ImplicitFuncs; 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.jassIm.*; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; +import fj.data.Either; import org.eclipse.jdt.annotation.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Stream; import static de.peeeq.wurstscript.types.VariablePosition.NONE; @@ -20,8 +21,10 @@ public class WurstTypeBoundTypeParam extends WurstType { private final TypeParamDef typeParamDef; private final WurstType baseType; + // the fromIndex and toIndex functions for old-generics: private FuncDef fromIndex; private FuncDef toIndex; + private final @Nullable Map typeConstraintFunctions; private boolean indexInitialized = false; private Element context; @@ -32,6 +35,11 @@ public WurstTypeBoundTypeParam(TypeParamDef def, WurstType baseType, Element con this.typeParamDef = def; this.baseType = baseType; this.context = context; + if (def.getTypeParamConstraints() instanceof NoTypeParamConstraints) { + this.typeConstraintFunctions = null; + } else { + this.typeConstraintFunctions = new HashMap<>(); + } } @Override @@ -56,8 +64,8 @@ public WurstType getBaseType() { } @Override - public ImType imTranslateType() { - return baseType.imTranslateType(); + public ImType imTranslateType(ImTranslator tr) { + return baseType.imTranslateType(tr); } @Override @@ -122,14 +130,16 @@ private void initIndex() { if (indexInitialized) { return; } - // if type does support generics natively, try to find implicit conversion functions - if (!baseType.supportsGenerics()) { - fromIndex = ImplicitFuncs.findFromIndexFunc(baseType, context); - toIndex = ImplicitFuncs.findToIndexFunc(baseType, context); - } else if (baseType instanceof WurstTypeBoundTypeParam) { - WurstTypeBoundTypeParam bt = (WurstTypeBoundTypeParam) baseType; - fromIndex = bt.getFromIndex(); - toIndex = bt.getToIndex(); + if (typeConstraintFunctions == null) { + if (!baseType.supportsGenerics()) { + // if type does support generics natively, try to find implicit conversion functions + fromIndex = ImplicitFuncs.findFromIndexFunc(baseType, context); + toIndex = ImplicitFuncs.findToIndexFunc(baseType, context); + } else if (baseType instanceof WurstTypeBoundTypeParam) { + WurstTypeBoundTypeParam bt = (WurstTypeBoundTypeParam) baseType; + fromIndex = bt.getFromIndex(); + toIndex = bt.getToIndex(); + } } indexInitialized = true; } @@ -140,6 +150,11 @@ public boolean supportsGenerics() { || getFromIndex() != null && getToIndex() != null; } + @Override + protected boolean isNullable() { + return baseType.isNullable(); + } + @Override public WurstTypeBoundTypeParam setTypeArgs(VariableBinding typeParamMapping) { return this.withBaseType(baseType.setTypeArgs(typeParamMapping)); @@ -175,4 +190,18 @@ public boolean isTranslatedToInt() { return baseType.isTranslatedToInt(); } + public @Nullable Map getTypeConstraintFunctions() { + return typeConstraintFunctions; + } + + public boolean isTemplateTypeParameter() { + return typeParamDef.getTypeParamConstraints() instanceof TypeExprList; + } + + public ImTypeArgument imTranslateToTypeArgument(ImTranslator tr) { + ImType t = imTranslateType(tr); + Map> typeClassBinding = new HashMap<>(); + // TODO add type class binding + return JassIm.ImTypeArgument(t, typeClassBinding); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClass.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClass.java index df97cabe3..fb9c5196c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClass.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClass.java @@ -2,9 +2,9 @@ import com.google.common.collect.ImmutableList; import de.peeeq.wurstscript.ast.*; -import de.peeeq.wurstscript.jassIm.ImExprOpt; -import de.peeeq.wurstscript.jassIm.ImType; -import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.ast.Element; +import de.peeeq.wurstscript.jassIm.*; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import fj.data.Option; import org.eclipse.jdt.annotation.Nullable; @@ -133,10 +133,6 @@ public WurstType replaceTypeVars(List newTypes) { return new WurstTypeClass(classDef, newTypes, isStaticRef()); } - @Override - public ImType imTranslateType() { - return TypesHelper.imInt(); - } @Override public ImExprOpt getDefaultValue() { 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 cb3346e87..0138306cc 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 @@ -2,12 +2,18 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; +import de.peeeq.wurstscript.ast.ClassOrInterface; import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.StructureDef; import de.peeeq.wurstscript.attributes.CheckHelper; 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.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 java.util.List; @@ -25,7 +31,7 @@ public WurstTypeClassOrInterface(List newTypes) { @Override - public abstract StructureDef getDef(); + public abstract @NonNull ClassOrInterface getDef(); /** * Level in the type hierarchy. @@ -123,4 +129,15 @@ VariableBinding matchAgainstSupertypeIntern(WurstType obj, @Nullable Element loc return null; } + @Override + public final ImType imTranslateType(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); + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClosure.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClosure.java index 7f9af51aa..b223c1c2f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClosure.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeClosure.java @@ -5,6 +5,7 @@ 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 org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -108,8 +109,8 @@ public String getFullName() { } @Override - public ImType imTranslateType() { - return WurstTypeInt.instance().imTranslateType(); + public ImType imTranslateType(ImTranslator tr) { + return WurstTypeInt.instance().imTranslateType(tr); } @Override @@ -117,6 +118,11 @@ public ImExprOpt getDefaultValue() { return JassIm.ImIntVal(0); } + @Override + protected boolean isNullable() { + return false; + } + public List getParamTypes() { return paramTypes; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeCode.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeCode.java index 8dea45382..586ca4370 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeCode.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeCode.java @@ -29,5 +29,10 @@ public ImExprOpt getDefaultValue() { return JassIm.ImNull(imTranslateType()); } + @Override + protected boolean isNullable() { + return true; + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeEnum.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeEnum.java index c4055b369..b1b428519 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeEnum.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeEnum.java @@ -1,11 +1,10 @@ package de.peeeq.wurstscript.types; -import com.google.common.collect.ImmutableMultimap; import de.peeeq.wurstscript.ast.EnumDef; -import de.peeeq.wurstscript.attributes.names.DefLink; 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 java.util.List; @@ -43,7 +42,7 @@ public WurstType replaceTypeVars(List newTypes) { @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { return TypesHelper.imInt(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeHandle.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeHandle.java index a9f5e7abf..c3ff707b1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeHandle.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeHandle.java @@ -30,5 +30,8 @@ public ImExprOpt getDefaultValue() { return JassIm.ImNull(imTranslateType()); } - + @Override + protected boolean isNullable() { + return true; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInfer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInfer.java index 63a4a5b01..de6b59a25 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInfer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeInfer.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.Nullable; /** @@ -36,7 +37,7 @@ public static WurstType instance() { } @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { throw new Error("not implemented"); } @@ -45,5 +46,10 @@ public ImExprOpt getDefaultValue() { throw new Error("not implemented"); } + @Override + protected boolean isNullable() { + return false; + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeIntLiteral.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeIntLiteral.java index f086d4cd7..8f8ae972f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeIntLiteral.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeIntLiteral.java @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImSimpleType; import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.Nullable; @@ -34,7 +35,7 @@ public ImExprOpt getDefaultValue() { } @Override - public ImSimpleType imTranslateType() { + public ImSimpleType imTranslateType(ImTranslator tr) { return TypesHelper.imInt(); } 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 9e61672ee..e0b392d68 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 @@ -1,19 +1,14 @@ package de.peeeq.wurstscript.types; import com.google.common.collect.ImmutableList; -import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.InterfaceDef; -import de.peeeq.wurstscript.ast.TypeParamDef; -import de.peeeq.wurstscript.attributes.names.TypeLink; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.jassIm.ImTypeArguments; import de.peeeq.wurstscript.jassIm.JassIm; -import fj.data.TreeMap; -import org.eclipse.jdt.annotation.Nullable; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; -import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; public class WurstTypeInterface extends WurstTypeClassOrInterface { @@ -80,11 +75,6 @@ public ImmutableList extendedInterfaces() { } - @Override - public ImType imTranslateType() { - return TypesHelper.imInt(); - } - @Override public ImExprOpt getDefaultValue() { return JassIm.ImNull(TypesHelper.imInt()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModule.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModule.java index ba542c019..dd04dbab6 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModule.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModule.java @@ -6,6 +6,7 @@ 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 org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -67,13 +68,13 @@ public WurstType replaceTypeVars(List newTypes) { } @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { return TypesHelper.imInt(); } @Override public ImExprOpt getDefaultValue() { - return JassIm.ImNull(imTranslateType()); + return JassIm.ImNull(TypesHelper.imInt()); } @Override diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModuleInstanciation.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModuleInstanciation.java index e41aa5fef..570808a78 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModuleInstanciation.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeModuleInstanciation.java @@ -6,6 +6,7 @@ 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 org.eclipse.jdt.annotation.Nullable; import java.util.List; @@ -83,7 +84,7 @@ public WurstType replaceTypeVars(List newTypes) { } @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { return TypesHelper.imInt(); } 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 fe43cd460..1e1b364b3 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 @@ -5,6 +5,7 @@ import com.google.common.collect.Lists; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.*; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import java.util.*; @@ -97,7 +98,7 @@ public VariableBinding getTypeArgBinding() { } @Override - public WurstType setTypeArgs(VariableBinding typeParamBounds) { + public WurstType setTypeArgs(@NonNull VariableBinding typeParamBounds) { List newTypes = Lists.newArrayList(); for (WurstTypeBoundTypeParam t : typeParameters) { newTypes.add(t.setTypeArgs(typeParamBounds)); @@ -230,5 +231,10 @@ public boolean isNestedInside(WurstType other) { return false; } + @Override + protected boolean isNullable() { + return true; + } + } \ No newline at end of file diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNull.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNull.java index 034735f6b..8539f8539 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNull.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeNull.java @@ -18,20 +18,26 @@ protected WurstTypeNull() { @Override VariableBinding matchAgainstSupertypeIntern(WurstType other, @Nullable Element location, VariableBinding mapping, VariablePosition variablePosition) { - if (other instanceof WurstTypeNull - || other instanceof WurstTypeHandle - || other instanceof WurstNativeType - || other instanceof WurstTypeString - || other instanceof WurstTypeCode - || other instanceof WurstTypeClass - || other instanceof WurstTypeInterface - || other instanceof WurstTypeModule - || other instanceof WurstTypeModuleInstanciation - || other instanceof WurstTypeTypeParam - || other instanceof WurstTypeBoundTypeParam - || Utils.isJassCode(location) && (other instanceof WurstTypeInt || other instanceof WurstTypeIntLiteral)) { + if (other.isNullable()) { return mapping; } + if (Utils.isJassCode(location) && (other instanceof WurstTypeInt || other instanceof WurstTypeIntLiteral)) { + return mapping; + } + +// other instanceof WurstTypeNull +// || other instanceof WurstTypeHandle +// || other instanceof WurstNativeType +// || other instanceof WurstTypeString +// || other instanceof WurstTypeCode +// || other instanceof WurstTypeClass +// || other instanceof WurstTypeInterface +// || other instanceof WurstTypeModule +// || other instanceof WurstTypeModuleInstanciation +// || +// other instanceof WurstTypeTypeParam +// other instanceof WurstTypeBoundTypeParam + return null; } @@ -45,5 +51,8 @@ public ImExprOpt getDefaultValue() { return JassIm.ImIntVal(0); } - + @Override + protected boolean isNullable() { + return true; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePackage.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePackage.java index c50c53dd4..10414bf92 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePackage.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePackage.java @@ -1,15 +1,12 @@ package de.peeeq.wurstscript.types; -import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.ast.NamedScope; import de.peeeq.wurstscript.ast.WPackage; -import de.peeeq.wurstscript.attributes.names.FuncLink; -import de.peeeq.wurstscript.attributes.names.NameLink; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import java.util.List; -import java.util.stream.Stream; public class WurstTypePackage extends WurstTypeNamedScope { @@ -44,7 +41,7 @@ public WurstType replaceTypeVars(List newTypes) { } @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { throw new Error("not implemented"); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePrimitive.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePrimitive.java index 65e78ef14..addb52d3b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePrimitive.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypePrimitive.java @@ -2,6 +2,7 @@ import de.peeeq.wurstscript.jassIm.ImSimpleType; import de.peeeq.wurstscript.jassIm.JassIm; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; public abstract class WurstTypePrimitive extends WurstType { @@ -24,8 +25,16 @@ public String getFullName() { } @Override + public ImSimpleType imTranslateType(ImTranslator tr) { + return imType; + } + public ImSimpleType imTranslateType() { return imType; } + @Override + protected boolean isNullable() { + return false; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeStaticTypeRef.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeStaticTypeRef.java index 757c8a188..d699280b4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeStaticTypeRef.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeStaticTypeRef.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.Nullable; public class WurstTypeStaticTypeRef extends WurstType { @@ -32,8 +33,8 @@ public String getFullName() { } @Override - public ImType imTranslateType() { - return base.imTranslateType(); + public ImType imTranslateType(ImTranslator tr) { + return base.imTranslateType(tr); } @Override @@ -41,6 +42,11 @@ public ImExprOpt getDefaultValue() { return base.getDefaultValue(); } + @Override + protected boolean isNullable() { + return false; + } + @Override public WurstType dynamic() { return base; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeString.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeString.java index 692a88bfe..1ba45d57e 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeString.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeString.java @@ -40,4 +40,10 @@ public ImExprOpt getDefaultValue() { return JassIm.ImStringVal(""); } + + @Override + protected boolean isNullable() { + return true; + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTuple.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTuple.java index eccc313e4..6257dd727 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTuple.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeTuple.java @@ -6,6 +6,7 @@ import de.peeeq.wurstscript.ast.*; 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.jdt.annotation.Nullable; @@ -50,11 +51,11 @@ public String getFullName() { @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { List types = Lists.newArrayList(); List names = Lists.newArrayList(); for (WParameter p : tupleDef.getParameters()) { - ImType pt = p.attrTyp().imTranslateType(); + ImType pt = p.attrTyp().imTranslateType(tr); types.add(pt); names.add(p.getName()); } @@ -70,6 +71,11 @@ public ImExprOpt getDefaultValue() { return JassIm.ImTupleExpr(exprs); } + @Override + protected boolean isNullable() { + return false; + } + public int getTupleIndex(VarDef varDef) { WParameter v = (WParameter) varDef; int index = tupleDef.getParameters().indexOf(v); 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 2cca279ad..f75c7e874 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,10 +1,13 @@ 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.attributes.CompileError; 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 fj.data.Option; import org.eclipse.jdt.annotation.Nullable; @@ -66,19 +69,32 @@ public WurstType setTypeArgs(VariableBinding typeParamBounds) { } @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { + if (hasTypeConstraints()) { + return JassIm.ImTypeVarRef(tr.getTypeVar(def)); + } return TypesHelper.imInt(); } + /** Using the new template generics with type constraints*/ + private boolean hasTypeConstraints() { + return def.getTypeParamConstraints() instanceof TypeExprList; + } + @Override public ImExprOpt getDefaultValue() { - return JassIm.ImNull(imTranslateType()); + throw new CompileError(def, "Cannot get default value for generic " + def.getName()); } @Override public boolean isCastableToInt() { - return true; + return !hasTypeConstraints(); + } + + @Override + protected boolean isNullable() { + return !hasTypeConstraints(); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnion.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnion.java index f6446dd87..982d99eea 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnion.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnion.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.Nullable; public class WurstTypeUnion extends WurstType { @@ -44,9 +45,9 @@ public String getFullName() { } @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { // TODO union of typeA and typeB - return typeA.imTranslateType(); + return typeA.imTranslateType(tr); } @Override @@ -54,6 +55,11 @@ public ImExprOpt getDefaultValue() { return typeA.getDefaultValue(); } + @Override + protected boolean isNullable() { + return typeA.isNullable() || typeB.isNullable(); + } + public WurstType getTypeA() { return typeA; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnknown.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnknown.java index 7af8c7cae..399ac4016 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnknown.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeUnknown.java @@ -4,6 +4,7 @@ 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 org.eclipse.jdt.annotation.Nullable; @@ -47,7 +48,7 @@ public static WurstTypeUnknown instance() { @Override - public ImType imTranslateType() { + public ImType imTranslateType(ImTranslator tr) { throw new Error("not implemented"); } @@ -56,4 +57,9 @@ public ImExprOpt getDefaultValue() { return JassIm.ImNoExpr(); } + @Override + protected boolean isNullable() { + return false; + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVararg.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVararg.java index c814f9df6..32aa518ae 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVararg.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVararg.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.ast.Element; import de.peeeq.wurstscript.jassIm.ImExprOpt; import de.peeeq.wurstscript.jassIm.ImType; +import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import org.eclipse.jdt.annotation.Nullable; @@ -39,8 +40,8 @@ public String getFullName() { @Override - public ImType imTranslateType() { - return baseType.imTranslateType(); + public ImType imTranslateType(ImTranslator tr) { + return baseType.imTranslateType(tr); } @@ -49,6 +50,11 @@ public ImExprOpt getDefaultValue() { throw new Error(); } + @Override + protected boolean isNullable() { + return false; + } + @Override public WurstType setTypeArgs(VariableBinding t) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVoid.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVoid.java index 5f6626f58..7a4f36814 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVoid.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/WurstTypeVoid.java @@ -4,6 +4,7 @@ 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 org.eclipse.jdt.annotation.Nullable; @@ -38,6 +39,10 @@ public static WurstTypeVoid instance() { } @Override + public ImType imTranslateType(ImTranslator tr) { + return JassIm.ImVoid(); + } + public ImType imTranslateType() { return JassIm.ImVoid(); } @@ -52,4 +57,9 @@ public boolean isVoid() { return true; } + @Override + protected boolean isNullable() { + return false; + } + } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Constants.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Constants.java new file mode 100644 index 000000000..915df635b --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/utils/Constants.java @@ -0,0 +1,8 @@ +package de.peeeq.wurstscript.utils; + +public class Constants { + /** + * The maximum size of arrays in Jass + */ + public static final int MAX_ARRAY_SIZE = 32768; +} 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 22efc2f78..7dbdac319 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 @@ -6,7 +6,6 @@ import de.peeeq.wurstscript.attributes.CofigOverridePackages; import de.peeeq.wurstscript.attributes.CompileError; import de.peeeq.wurstscript.attributes.ImplicitFuncs; -import de.peeeq.wurstscript.attributes.SmallHelpers; import de.peeeq.wurstscript.attributes.names.DefLink; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; @@ -17,7 +16,6 @@ import de.peeeq.wurstscript.validation.controlflow.DataflowAnomalyAnalysis; import de.peeeq.wurstscript.validation.controlflow.ReturnsAnalysis; import fj.P2; -import fj.data.TreeMap; import org.eclipse.jdt.annotation.Nullable; import java.util.*; @@ -1483,63 +1481,70 @@ public VariableBinding case_ExprMemberMethodDotDot(ExprMemberMethodDotDot e) { for (P2 t : mapping) { WurstTypeBoundTypeParam boundTyp = t._2(); WurstType typ = boundTyp.getBaseType(); - if (!typ.isTranslatedToInt() && !(e instanceof ModuleUse)) { - String toIndexFuncName = ImplicitFuncs.toIndexFuncName(typ); - String fromIndexFuncName = ImplicitFuncs.fromIndexFuncName(typ); - Collection toIndexFuncs = ImplicitFuncs.findToIndexFuncs(typ, e); - Collection fromIndexFuncs = ImplicitFuncs.findFromIndexFuncs(typ, e); - if (toIndexFuncs.isEmpty()) { - e.addError("Type parameters can only be bound to ints and class types, but " + "not to " + typ - + ".\n" + "You can provide functions " + toIndexFuncName + " and " + fromIndexFuncName - + " to use this type " + "with generics."); - } else if (fromIndexFuncs.isEmpty()) { - e.addError("Could not find function " + fromIndexFuncName + " which is required to use " + typ - + " with generics."); - } else { - if (toIndexFuncs.size() > 1) { - e.addError("There is more than one function named " + toIndexFuncName); - } - if (fromIndexFuncs.size() > 1) { - e.addError("There is more than one function named " + fromIndexFuncName); - } - NameDef toIndex = Utils.getFirst(toIndexFuncs).getDef(); - if (toIndex instanceof FuncDef) { - FuncDef toIndexF = (FuncDef) toIndex; - if (toIndexF.getParameters().size() != 1) { - toIndexF.addError("Must have exactly one parameter"); - - } else if (!toIndexF.getParameters().get(0).attrTyp().equalsType(typ, e)) { - toIndexF.addError("Parameter must be of type " + typ); + TypeParamDef tp = t._1(); + if (tp.getTypeParamConstraints() instanceof TypeExprList) { + // new style generics + } else { // old style generics + + if (!typ.isTranslatedToInt() && !(e instanceof ModuleUse)) { + String toIndexFuncName = ImplicitFuncs.toIndexFuncName(typ); + String fromIndexFuncName = ImplicitFuncs.fromIndexFuncName(typ); + Collection toIndexFuncs = ImplicitFuncs.findToIndexFuncs(typ, e); + Collection fromIndexFuncs = ImplicitFuncs.findFromIndexFuncs(typ, e); + if (toIndexFuncs.isEmpty()) { + e.addError("Type parameters can only be bound to ints and class types, but " + "not to " + typ + + ".\n" + "You can provide functions " + toIndexFuncName + " and " + fromIndexFuncName + + " to use this type " + "with generics."); + } else if (fromIndexFuncs.isEmpty()) { + e.addError("Could not find function " + fromIndexFuncName + " which is required to use " + typ + + " with generics."); + } else { + if (toIndexFuncs.size() > 1) { + e.addError("There is more than one function named " + toIndexFuncName); } - - WurstType returnType = toIndexF.attrReturnTyp(); - if (!returnType.equalsType(WurstTypeInt.instance(), e)) { - toIndexF.addError("Return type must be of type int " + " but was " + returnType); + if (fromIndexFuncs.size() > 1) { + e.addError("There is more than one function named " + fromIndexFuncName); } - } else { - toIndex.addError("This should be a function."); - } + NameDef toIndex = Utils.getFirst(toIndexFuncs).getDef(); + if (toIndex instanceof FuncDef) { + FuncDef toIndexF = (FuncDef) toIndex; - NameDef fromIndex = Utils.getFirst(fromIndexFuncs).getDef(); - if (fromIndex instanceof FuncDef) { - FuncDef fromIndexF = (FuncDef) fromIndex; + if (toIndexF.getParameters().size() != 1) { + toIndexF.addError("Must have exactly one parameter"); - if (fromIndexF.getParameters().size() != 1) { - fromIndexF.addError("Must have exactly one parameter"); + } else if (!toIndexF.getParameters().get(0).attrTyp().equalsType(typ, e)) { + toIndexF.addError("Parameter must be of type " + typ); + } - } else if (!fromIndexF.getParameters().get(0).attrTyp() - .equalsType(WurstTypeInt.instance(), e)) { - fromIndexF.addError("Parameter must be of type int"); + WurstType returnType = toIndexF.attrReturnTyp(); + if (!returnType.equalsType(WurstTypeInt.instance(), e)) { + toIndexF.addError("Return type must be of type int " + " but was " + returnType); + } + } else { + toIndex.addError("This should be a function."); } - WurstType returnType = fromIndexF.attrReturnTyp(); - if (!returnType.equalsType(typ, e)) { - fromIndexF.addError("Return type must be of type " + typ + " but was " + returnType); - } + NameDef fromIndex = Utils.getFirst(fromIndexFuncs).getDef(); + if (fromIndex instanceof FuncDef) { + FuncDef fromIndexF = (FuncDef) fromIndex; - } else { - fromIndex.addError("This should be a function."); + if (fromIndexF.getParameters().size() != 1) { + fromIndexF.addError("Must have exactly one parameter"); + + } else if (!fromIndexF.getParameters().get(0).attrTyp() + .equalsType(WurstTypeInt.instance(), e)) { + fromIndexF.addError("Parameter must be of type int"); + } + + WurstType returnType = fromIndexF.attrReturnTyp(); + if (!returnType.equalsType(typ, e)) { + fromIndexF.addError("Return type must be of type " + typ + " but was " + returnType); + } + + } else { + fromIndex.addError("This should be a function."); + } } } } 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 new file mode 100644 index 000000000..d94e0137f --- /dev/null +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java @@ -0,0 +1,1258 @@ +package tests.wurstscript.tests; + +import org.testng.annotations.Ignore; +import org.testng.annotations.Test; + +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" + ); + } + + @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" + ); + } + + @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" + ); + } + + @Test + @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" + ); + } + + @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" + ); + } + + @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" + ); + } + + + @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" + ); + } + + @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()" + ); + } + + + + + + @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" + ); + } + + + @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" + ); + } + + @Test + public void testSubtypeGenericClass() { + testAssertOkLines(false, + "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" + ); + } + + @Test + public void testSubtypeGenericInterface() { + testAssertOkLines(false, + "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" + ); + } + + @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" + ); + } + + + @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" + ); + } + + @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" + ); + } + + @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" + ); + } + + @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" + ); + } + + @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 )" + ); + } + + @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)" + ); + } + + @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" + ); + } + + + @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" + ); + } + + @Test + public void nativeTypes() { + testAssertOkLines(false, + "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", + + "class List", + " function get() returns T", + " return 0 castTo T", + + "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" + ); + } + + @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()" + ); + } + + @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)" + ); + } + + @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)" + ); + } + + @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)" + ); + } + + @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" + + ); + } + + @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" + + ); + } + + + @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" + + ); + } + + @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" + + ); + } + + + @Test + public void genericMethod1() { + testAssertOkLines(false, + "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()" + ); + } + + @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" + ); + } + + @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" + ); + } + + @Test + public void genericRecursive() { + testAssertOkLines(false, + "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" + ); + } + + @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" + ); + } + + @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" + ); + } + + @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>>>" + ); + } + + @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>>>>" + ); + } + + @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>>" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + + @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()" + ); + } + + @Test + @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()" + ); + } + + @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)" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + @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\")" + ); + } + + @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()" + ); + } + + @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)" + ); + } + + @Test + @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()" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + + @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()" + ); + } + + @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)" + ); + } + + @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()" + ); + } + + @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()" + ); + } + + @Test + public void missingTypeArgsConstructor() { + testAssertErrorsLines(false, "Cannot infer type for type parameter T", + "package test", + "class C", + "init", + " let c = new C" + ); + } + + + @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()" + ); + } + + @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()" + ); + } + + @Test + public void tooManyTypeArgsConstructor() { + testAssertErrorsLines(false, "Too many type arguments given", + "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()" + ); + } + + @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()" + ); + } + + @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" + ); + } + + +}