Skip to content

Commit 4df540c

Browse files
committed
Carry type arguments through the vararg call redirects
Both redirects rebuilt the call with an empty ImTypeArguments. That was right on Jass, where EliminateGenerics runs at phase 2 and nothing generic survives to the vararg phase, but it is an assumption about the pipeline written into a node constructor, and Lua reaches this pass with a different amount erased. Measured before changing anything, since the reported failure is a strong claim: a probe that threw whenever either redirect saw a non-empty type argument list found no hit anywhere in LuaBackendAuditTests, VarargTests, GenericsTests, LuaTranslationTests, GenericsWithTypeclassesTests or StdLibOwnTests. A generic vararg returning its type parameter, consumed by string concatenation, also produces the correct string without this change. So the reported miscompilation does not occur: Lua erases these before the pass rather than carrying them into it. The list is moved across anyway. It costs nothing, it is a no-op while the list is empty, and rebuilding a call should copy what the call had instead of restating a fact about phase order that only holds on one target.
1 parent 2dbba0c commit 4df540c

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/VarargEliminator.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ private static List<ImExpr> receiverAndArguments(ImMethodCall call) {
168168
private void redirectMethodCall(ImMethodCall call, ImFunction newFunc) {
169169
ImExprs args = JassIm.ImExprs(call.getReceiver().copy());
170170
args.addAll(call.getArguments().removeAll());
171-
call.replaceBy(JassIm.ImFunctionCall(call.getTrace(), newFunc, JassIm.ImTypeArguments(), args,
171+
call.replaceBy(JassIm.ImFunctionCall(call.getTrace(), newFunc,
172+
JassIm.ImTypeArguments(call.getTypeArguments().removeAll()), args,
172173
call.getTuplesEliminated(), CallType.NORMAL));
173174
}
174175

@@ -350,7 +351,13 @@ public void visit(ImVarAccess va) {
350351

351352
private void redirectCall(ImFunctionCall call, ImFunction newFunc) {
352353
// Redirect call to new function
353-
ImFunctionCall newCall = JassIm.ImFunctionCall(call.getTrace(), newFunc, JassIm.ImTypeArguments(), JassIm.ImExprs(call.getArguments().removeAll()), call.getTuplesEliminated(), call.getCallType());
354+
// Carry the type arguments over rather than assuming there are none. Jass erases generics
355+
// long before this pass, so an empty list was always right there; on Lua the erasure happens
356+
// elsewhere and this list is empty in practice too, but rebuilding the call should not be
357+
// the step that decides that.
358+
ImFunctionCall newCall = JassIm.ImFunctionCall(call.getTrace(), newFunc,
359+
JassIm.ImTypeArguments(call.getTypeArguments().removeAll()),
360+
JassIm.ImExprs(call.getArguments().removeAll()), call.getTuplesEliminated(), call.getCallType());
354361
call.replaceBy(newCall);
355362
}
356363

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,6 +2129,29 @@ public void varargConstructorAboveTheLuaArityBoundKeepsThePackedPath() {
21292129
);
21302130
}
21312131

2132+
/**
2133+
* Jass erases generics long before this pass, so `redirectCall` could build the replacement with
2134+
* an empty type-argument list. Lua only specialises concrete operations at that point and leaves
2135+
* generics live, so dropping them leaves the redirected call typed by an unresolved type variable.
2136+
* `LuaNativeLowering` decides string concatenation from each operand's type, so a generic vararg
2137+
* returning its type parameter silently became a numeric addition on strings.
2138+
*/
2139+
@Test
2140+
public void genericVarargCallKeepsItsTypeArgumentsOnLua() {
2141+
test().testLua(true).withStdLib().executeProg().lines(
2142+
"package Test",
2143+
"function lastOf<T>(vararg T xs) returns T",
2144+
" T result = null",
2145+
" for x in xs",
2146+
" result = x",
2147+
" return result",
2148+
"init",
2149+
" let joined = \"a\" + lastOf<string>(\"b\", \"c\")",
2150+
" if joined == \"ac\" and lastOf<int>(1, 2) == 2",
2151+
" testSuccess()"
2152+
);
2153+
}
2154+
21322155
@Test
21332156
public void virtuallyDispatchedVarargMethodKeepsThePackedPath() throws IOException {
21342157
test().testLua(true).executeProg().lines(

0 commit comments

Comments
 (0)