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 f20c27bfb..8273e2179 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 @@ -340,7 +340,7 @@ exprMemberVar: exprVarAccess: - varname=ID indexes? + varname=(ID|IT) indexes? ; @@ -375,7 +375,7 @@ exprPrimary: | exprClosure | exprStatementsBlock | exprDestroy - | varname=ID indexes? + | varname=(ID|IT) indexes? | atom=(INT | REAL | STRING diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrClosureCapturedVariables.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrClosureCapturedVariables.java index eb21d2184..cab743a2f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrClosureCapturedVariables.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrClosureCapturedVariables.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMultimap.Builder; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.types.WurstTypeArray; import java.util.Map.Entry; @@ -33,6 +34,10 @@ private static void collect(Builder result, ExprClosure closure if (e instanceof NameRef) { NameRef nr = (NameRef) e; NameLink def = nr.attrNameLink(); + if (def instanceof OtherLink) { + // Synthetic links (e.g. implicit closure-self) are not captured locals. + return; + } if (def != null && isLocalVariable(def.getDef())) { VarDef v = (VarDef) def.getDef(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrConstantValue.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrConstantValue.java index ee990348e..a59cf251b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrConstantValue.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrConstantValue.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.WurstOperator; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.intermediatelang.ILconst; import de.peeeq.wurstscript.intermediatelang.ILconstInt; @@ -31,6 +32,9 @@ public static ILconst calculate(ExprIntVal e) { public static ILconst calculate(ExprVarAccess e) { NameLink v = e.attrNameLink(); + if (v instanceof OtherLink) { + throw new ConstantValueCalculationException(e.toString()); + } if (v != null && v.getDef() instanceof GlobalVarDef) { GlobalVarDef g = (GlobalVarDef) v.getDef(); if (g.attrIsConstant() && g.getInitialExpr() instanceof Expr) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrExprType.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrExprType.java index 817b74a47..98239cd59 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrExprType.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrExprType.java @@ -5,6 +5,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.types.*; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; @@ -68,13 +69,13 @@ public static WurstType calculate(ExprVarAccess term) { return WurstTypeUnknown.instance(); } - if (varDef.getDef() instanceof VarDef) { + if (!(varDef instanceof OtherLink) && varDef.getDef() instanceof VarDef) { if (Utils.getParentVarDef(Optional.of(term)) == Optional.of((VarDef) varDef.getDef())) { term.addError("Recursive variable definition is not allowed."); return WurstTypeUnknown.instance(); } } - if (varDef.getDef() instanceof FunctionDefinition) { + if (!(varDef instanceof OtherLink) && varDef.getDef() instanceof FunctionDefinition) { term.addError("Missing parantheses for function call"); } return varDef.getTyp(); @@ -388,10 +389,10 @@ public static WurstType calculate(ExprMemberVarDot term) { if (varDef == null) { return WurstTypeUnknown.instance(); } - if (varDef.getDef() instanceof FunctionDefinition) { + if (!(varDef instanceof OtherLink) && varDef.getDef() instanceof FunctionDefinition) { term.addError("Missing parantheses for function call"); } - if (varDef.getDef().attrIsStatic() && !term.getLeft().attrTyp().isStaticRef()) { + if (!(varDef instanceof OtherLink) && varDef.getDef().attrIsStatic() && !term.getLeft().attrTyp().isStaticRef()) { term.addError("Cannot access static variable " + term.getVarName() + " via a dynamic reference."); } return varDef.getTyp(); // TODO .setTypeArgs(term.getLeft().attrTyp().getTypeArgBinding()); @@ -403,7 +404,7 @@ public static WurstType calculate(ExprMemberArrayVarDot term) { if (varDef == null) { return WurstTypeUnknown.instance(); } - if (varDef.getDef().attrIsStatic() && !term.getLeft().attrTyp().isStaticRef()) { + if (!(varDef instanceof OtherLink) && varDef.getDef().attrIsStatic() && !term.getLeft().attrTyp().isStaticRef()) { term.addError("Cannot access static array variable " + term.getVarName() + " via a dynamic reference."); } WurstType typ = varDef.getTyp(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java index c9b3720b4..e195e86e4 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrImplicitParameter.java @@ -3,6 +3,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.types.WurstType; import org.eclipse.jdt.annotation.Nullable; @@ -108,6 +109,9 @@ static OptExpr getFunctionCallImplicitParameter(FunctionCall e, FuncLink calledF private static OptExpr getImplicitParamterCaseNormalVar(NameRef e) { NameLink def = e.attrNameLink(); + if (def instanceof OtherLink) { + return Ast.NoExpr(); + } if (def != null && def.getDef() instanceof VarDef) { VarDef varDef = (VarDef) def.getDef(); if (varDef.attrIsDynamicClassMember()) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNameDef.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNameDef.java index 5559708ac..e30419418 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNameDef.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrNameDef.java @@ -3,7 +3,11 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; +import de.peeeq.wurstscript.attributes.names.Visibility; import de.peeeq.wurstscript.types.WurstType; +import de.peeeq.wurstscript.types.WurstTypeClassOrInterface; +import de.peeeq.wurstscript.types.WurstTypeUnknown; import de.peeeq.wurstscript.types.WurstTypeEnum; import de.peeeq.wurstscript.types.WurstTypeModule; import org.eclipse.jdt.annotation.Nullable; @@ -74,8 +78,52 @@ public static NameLink calculate(ExprMemberVar term) { protected static NameLink searchNameInScope(String varName, NameRef node) { boolean showErrors = !varName.startsWith("gg_"); - NameLink result = node.lookupVar(varName, showErrors); - return result; + if (!"it".equals(varName)) { + return node.lookupVar(varName, showErrors); + } + + // Normal lexical lookup wins, so user-defined names can shadow implicit closure-self. + NameLink result = node.lookupVar(varName, false); + if (result != null) { + return result; + } + + NameLink implicitClosureSelf = lookupImplicitClosureSelf(node, showErrors); + if (implicitClosureSelf != null) { + return implicitClosureSelf; + } + if (node.attrNearestExprClosure() != null) { + // keep diagnostics focused on closure typing instead of "unknown variable it" + return null; + } + + if (!showErrors) { + return null; + } + + // Fallback to default diagnostics when no implicit closure-self is available. + return node.lookupVar(varName, true); + } + + private static @Nullable NameLink lookupImplicitClosureSelf(NameRef node, boolean showErrors) { + ExprClosure closure = node.attrNearestExprClosure(); + if (closure == null) { + return null; + } + WurstType expectedTyp = closure.attrExpectedTypAfterOverloading(); + if (!(expectedTyp instanceof WurstTypeClassOrInterface)) { + if (showErrors && (expectedTyp instanceof WurstTypeUnknown)) { + node.addError("Cannot use implicit closure self 'it' because the closure target type is unknown."); + } + return null; + } + + return new OtherLink(Visibility.LOCAL, "it", expectedTyp) { + @Override + public de.peeeq.wurstscript.jassIm.ImExpr translate(NameRef e, de.peeeq.wurstscript.translation.imtranslation.ImTranslator t, de.peeeq.wurstscript.jassIm.ImFunction f) { + return de.peeeq.wurstscript.jassIm.JassIm.ImVarAccess(t.getThisVar(closure)); + } + }; } private static boolean isWriteAccess(final NameRef node) { @@ -110,7 +158,7 @@ private static boolean isWriteAccess(final NameRef node) { public static @Nullable NameDef tryGetNameDef(NameRef e) { NameLink link = e.attrNameLink(); - if (link == null) { + if (link == null || link instanceof OtherLink) { return null; } return link.getDef(); @@ -139,6 +187,9 @@ private static boolean isWriteAccess(final NameRef node) { public static NameDef calculateDef(NameRef nameRef) { NameLink l = nameRef.attrNameLink(); + if (l instanceof OtherLink) { + return null; + } return l == null ? null : l.getDef(); } } 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 6ffd24e10..66b1132a8 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 @@ -4,6 +4,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; @@ -160,6 +161,9 @@ public static String description(NameRef nr) { if (nameDef == null) { return nr.getVarName() + " is not defined yet."; } + if (nameDef instanceof OtherLink) { + return nr.getVarName() + " has type " + htmlType(nameDef.getTyp()); + } return nameDef.getDef().descriptionHtml(); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java index e734b3143..4125de5da 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/ReadVariables.java @@ -33,7 +33,7 @@ private static ImmutableList generic(Element e) { } public static ImmutableList calculate(ExprVarAccess e) { - if (e.attrNameLink() != null) { + if (e.attrNameLink() != null && e.attrNameDef() != null) { return ImmutableList.of(e.attrNameDef()); } else { return ImmutableList.emptyList(); @@ -42,7 +42,7 @@ public static ImmutableList calculate(ExprVarAccess e) { public static ImmutableList calculate(ExprVarArrayAccess e) { ImmutableList r = ImmutableList.emptyList(); - if (e.attrNameLink() != null) { + if (e.attrNameLink() != null && e.attrNameDef() != null) { r = ImmutableList.of(e.attrNameDef()); } r = r.cons(generic(e.getIndexes())); @@ -51,7 +51,7 @@ public static ImmutableList calculate(ExprVarArrayAccess e) { public static ImmutableList calculate(ExprMemberArrayVar e) { ImmutableList r = ImmutableList.emptyList(); - if (e.attrNameLink() != null) { + if (e.attrNameLink() != null && e.attrNameDef() != null) { r = ImmutableList.of(e.attrNameDef()); } r = r.cons(e.getLeft().attrReadVariables()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedGlobalVariables.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedGlobalVariables.java index b43f627d0..9be1ea92a 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedGlobalVariables.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedGlobalVariables.java @@ -5,6 +5,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.FuncLink; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.types.WurstType; import de.peeeq.wurstscript.types.WurstTypeClass; @@ -36,7 +37,7 @@ public static ImmutableList getUsedGlobals(ExprOrStatements e) { } else if (e instanceof NameRef) { NameRef nameRef = (NameRef) e; NameLink def = nameRef.attrNameLink(); - if (def.getDef() instanceof GlobalVarDef) { + if (def != null && !(def instanceof OtherLink) && def.getDef() instanceof GlobalVarDef) { GlobalVarDef varDef = (GlobalVarDef) def.getDef(); result.add(varDef); } @@ -103,7 +104,7 @@ private static void collectReadGlobals(Element e, Builder result) { // write access } else { NameLink def = nameRef.attrNameLink(); - if (def.getDef() instanceof GlobalVarDef) { + if (def != null && !(def instanceof OtherLink) && def.getDef() instanceof GlobalVarDef) { GlobalVarDef varDef = (GlobalVarDef) def.getDef(); result.add(varDef); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedPackages.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedPackages.java index dc890740d..62a48c4ef 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedPackages.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/UsedPackages.java @@ -8,6 +8,7 @@ import de.peeeq.wurstscript.ast.VarDef; import de.peeeq.wurstscript.ast.WPackage; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; public class UsedPackages { @@ -34,7 +35,7 @@ private static void processChildren(Element e, public static ImmutableCollection usedPackages(NameRef e) { ImmutableSet.Builder result = ImmutableSet.builder(); NameLink def = e.attrNameLink(); - if (def.getDef() instanceof VarDef) { + if (def != null && !(def instanceof OtherLink) && def.getDef() instanceof VarDef) { if (def.getDef().attrNearestPackage() instanceof WPackage) { result.add((WPackage) e.attrNearestPackage()); } 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 5d4715798..4abe4b137 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 @@ -247,6 +247,9 @@ public static ImExpr translateIntern(NameRef e, ImTranslator t, ImFunction f) { private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f) throws CompileError { NameLink link = e.attrNameLink(); + if (link instanceof OtherLink) { + return ((OtherLink) link).translate(e, t, f); + } NameDef decl = link == null ? null : link.getDef(); if (decl == null) { // should only happen with gg_ variables @@ -303,9 +306,6 @@ private static ImExpr translateNameDef(NameRef e, ImTranslator t, ImFunction f) EnumMember enumMember = (EnumMember) decl; int id = t.getEnumMemberId(enumMember); return ImIntVal(id); - } else if (link instanceof OtherLink) { - OtherLink otherLink = (OtherLink) link; - return otherLink.translate(e, t, f); } else { throw new CompileError(e.getSource(), "Cannot translate reference to " + Utils.printElement(decl)); } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index 51cbdde46..26626d111 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -775,9 +775,12 @@ private boolean rewriteTypeCastingCompatFunction(ImFunction f, LuaFunction lf) { if (f.getParameters().isEmpty()) { return false; } - String tcFunc = f.getName(); - ImVar p = f.getParameters().get(0); - LuaExpr arg = LuaAst.LuaExprVarAccess(luaVar.getFor(p)); + String tcFunc = getTypeCastingFunctionName(f); + if (tcFunc == null) { + return false; + } + ImVar firstParam = f.getParameters().get(0); + LuaExpr arg = LuaAst.LuaExprVarAccess(luaVar.getFor(firstParam)); if ("stringToIndex".equals(tcFunc)) { lf.getBody().clear(); @@ -805,17 +808,6 @@ private boolean rewriteTypeCastingCompatFunction(ImFunction f, LuaFunction lf) { lf.getBody().add(LuaAst.LuaReturn(LuaAst.LuaExprFunctionCall(fromIndexFunction, LuaAst.LuaExprlist(arg)))); return true; } - // Final fallback for transformed/copied function names that may lose trace info: - if (tcFunc.endsWith("ToIndex")) { - lf.getBody().clear(); - lf.getBody().add(LuaAst.LuaReturn(LuaAst.LuaExprFunctionCall(toIndexFunction, LuaAst.LuaExprlist(arg)))); - return true; - } - if (tcFunc.endsWith("FromIndex")) { - lf.getBody().clear(); - lf.getBody().add(LuaAst.LuaReturn(LuaAst.LuaExprFunctionCall(fromIndexFunction, LuaAst.LuaExprlist(arg)))); - return true; - } return false; } @@ -1100,13 +1092,6 @@ public String getTypeCastingFunctionName(ImFunction f) { return fd.getName(); } } - // Fallback: transformed/copied IM functions may lose package trace metadata. - // Keep Lua behavior stable by routing canonical *ToIndex/*FromIndex names anyway. - String name = f.getName(); - if ("stringToIndex".equals(name) || "stringFromIndex".equals(name) - || name.endsWith("ToIndex") || name.endsWith("FromIndex")) { - return name; - } return null; } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/ValidateLocalUsage.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/ValidateLocalUsage.java index 12e589b2e..0a2e9abf7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/ValidateLocalUsage.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/ValidateLocalUsage.java @@ -2,6 +2,7 @@ import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import java.util.Collection; import java.util.HashSet; @@ -42,7 +43,7 @@ public void visit(StmtSet set) { LExpr updatedExpr = set.getUpdatedExpr(); if (updatedExpr != null) { NameLink nameLink = updatedExpr.attrNameLink(); - if (nameLink != null) { + if (nameLink != null && !(nameLink instanceof OtherLink)) { locals.remove(nameLink.getDef()); } 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 f01f0dd55..297b38313 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 @@ -10,6 +10,7 @@ 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.attributes.names.OtherLink; import de.peeeq.wurstscript.attributes.names.VarLink; import de.peeeq.wurstscript.gui.ProgressHelper; import de.peeeq.wurstscript.types.*; @@ -300,7 +301,7 @@ private void collectUsedPackages(Set used, Element root) { if (e instanceof NameRef) { NameRef nr = (NameRef) e; NameLink def = nr.attrNameLink(); - if (def != null) { + if (def != null && !(def instanceof OtherLink)) { used.add(def.getDef().attrNearestPackage()); if (def.getDef().attrHasAnnotation("@config")) { WPackage configPackage = getConfiguredPackage(def.getDef()); @@ -1228,7 +1229,10 @@ private boolean refersToSameVar(OptExpr a, OptExpr b) { NameRef vb = (NameRef) b; NameLink nla = va.attrNameLink(); NameLink nlb = vb.attrNameLink(); - if (nla != null && nlb != null && nla.getDef() == nlb.getDef() + if (nla != null && nlb != null + && !(nla instanceof OtherLink) + && !(nlb instanceof OtherLink) + && nla.getDef() == nlb.getDef() && refersToSameVar(va.attrImplicitParameter(), vb.attrImplicitParameter())) { if (va instanceof AstElementWithIndexes && vb instanceof AstElementWithIndexes) { AstElementWithIndexes vai = (AstElementWithIndexes) va; @@ -2024,7 +2028,7 @@ private void visit(WImport wImport) { */ private void checkVarRef(NameRef e, boolean dynamicContext) { NameLink link = e.attrNameLink(); - if (link == null) { + if (link == null || link instanceof OtherLink) { return; } NameDef def = link.getDef(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/controlflow/DataflowAnomalyAnalysis.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/controlflow/DataflowAnomalyAnalysis.java index 9747becd7..4af2c9931 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/controlflow/DataflowAnomalyAnalysis.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/controlflow/DataflowAnomalyAnalysis.java @@ -8,6 +8,7 @@ import de.peeeq.immutablecollections.ImmutableList; import de.peeeq.wurstscript.ast.*; import de.peeeq.wurstscript.attributes.names.NameLink; +import de.peeeq.wurstscript.attributes.names.OtherLink; import de.peeeq.wurstscript.types.WurstTypeArray; import de.peeeq.wurstscript.utils.Utils; import org.eclipse.jdt.annotation.Nullable; @@ -360,7 +361,7 @@ private VarStates handleExprInCompound(VarStates incoming, Expr expr) { if (s instanceof StmtSet) { StmtSet s2 = (StmtSet) s; NameLink link = s2.getUpdatedExpr().attrNameLink(); - if (link != null) { + if (link != null && !(link instanceof OtherLink)) { n = link.getDef(); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClosureTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClosureTests.java index dcf418cb2..e027dd6a1 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClosureTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/ClosureTests.java @@ -68,6 +68,76 @@ public void closure_inferType() { ); } + @Test + public void closure_selfReference_withIt_recursion() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "interface IntFunc", + " function apply(int x) returns int", + "init", + " IntFunc fact = (int n) ->", + " if n <= 1", + " return 1", + " return n * it.apply(n - 1)", + " if fact.apply(5) == 120", + " testSuccess()"); + } + + @Test + public void closure_selfReference_withIt_usesNearestClosure() { + testAssertOkLines(true, + "package test", + "native testSuccess()", + "interface IntFunc", + " function apply(int x) returns int", + "init", + " IntFunc outer = (int x) -> begin", + " IntFunc inner = (int y) ->", + " if y <= 0", + " return 0", + " return 1 + it.apply(y - 1)", + " return inner.apply(x)", + " end", + " if outer.apply(3) == 3", + " testSuccess()"); + } + + @Test + public void closure_selfReference_withIt_requiresKnownClosureTargetType() { + testAssertErrorsLines(false, "closure target type is unknown", + "package test", + "init", + " let f = (int x) -> it"); + } + + @Test + public void closure_selfReference_withIt_isReadOnly() { + testAssertErrorsLines(false, "Invalid assignment. This is not a variable", + "package test", + "interface IntFunc", + " function apply(int x) returns int", + "init", + " IntFunc f = (int x) -> begin", + " it = f", + " return x", + " end"); + } + + @Test + public void closure_selfReference_withIt_assignmentNoCrash() { + testAssertOkLines(false, + "package test", + "interface IntFunc", + " function apply(int x) returns int", + "init", + " IntFunc f = (int n) -> begin", + " IntFunc x = it", + " x = it", + " return n", + " end"); + } + @Test public void closure_begin_end1() { testAssertOkLines(true, diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java index 0ab8e9976..f58f32afb 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java @@ -439,6 +439,30 @@ public void objectIndexFunctionsDoNotCollideWithUserFunctions() throws IOExcepti assertFunctionBodyContains(compiled, "testClass", "__wurst_objectFromIndex", true); } + @Test + public void tupleReturningMethodNamedFromIndexIsNotTypecastWrapper() throws IOException { + test().testLua(true).lines( + "package TupleFromIndexRepro", + "public tuple vec2(real x, real y)", + "public tuple searchResult(boolean found, vec2 pos)", + "public constant ZERO2 = vec2(0., 0.)", + "public class A", + " function next() returns searchResult", + " return nextFromIndex(1)", + " function nextFromIndex(int startIndex) returns searchResult", + " return searchResult(false, ZERO2)", + "public class B extends A", + "init", + " let b = new B()", + " let r = b.next()", + " if r.found", + " skip" + ); + String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_tupleReturningMethodNamedFromIndexIsNotTypecastWrapper.lua"), Charsets.UTF_8); + assertContainsRegex(compiled, "function\\s+[^\\n]*nextFromIndex[^\\n]*\\("); + assertDoesNotContainRegex(compiled, "return\\s+__wurst_objectFromIndex\\s*\\(\\s*this\\s*\\)"); + } + @Test public void oldGenericsCastingDoesNotUseGetHandleId() throws IOException { test().testLua(true).withStdLib().lines( @@ -481,6 +505,31 @@ public void newGenericsDoNotUseOldTypecastHelpersInLua() throws IOException { assertFunctionBodyContains(compiled, "testGeneric", "__wurst_objectFromIndex", false); } + @Test + public void newGenericsStringFieldAssignmentRoundTripsInLua() throws IOException { + test().testLua(true).lines( + "package Test", + "class C", + " T x", + "function stringToIndex(string s) returns int", + " return 42", + "function stringFromIndex(int i) returns string", + " return \"42\"", + "function testGenericStringField() returns boolean", + " C c = new C", + " c.x = \"42\"", + " return c.x == \"42\"", + "init", + " if testGenericStringField()", + " skip" + ); + String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_newGenericsStringFieldAssignmentRoundTripsInLua.lua"), Charsets.UTF_8); + assertFunctionBodyContains(compiled, "testGenericStringField", "c.C_x = \"42\"", true); + assertFunctionBodyContains(compiled, "testGenericStringField", "stringEnsure(c.C_x)", true); + assertFunctionBodyContains(compiled, "testGenericStringField", "__wurst_stringToIndex", false); + assertFunctionBodyContains(compiled, "testGenericStringField", "__wurst_stringFromIndex", false); + } + @Test public void largeFunctionSpillsLocalsIntoTableInLua() throws IOException { List lines = new ArrayList<>();