Skip to content

Commit 13eb5f5

Browse files
committed
Leave unreachable dispatches to garbage removal
Rejecting a dispatch whose function had no specialization also rejected a bounded generic that is merely declared and never called. Nothing has to supply a concrete type for such a function, and garbage removal deletes it later, so the rejection made an unused generic API fail to compile on Lua while remaining fine on Jass. Only the dispatches this pass can prove dead are neutralised now: those in a function which does have a specialization, since every reachable call was rewritten to it. Everything else is left for the passes that already decide reachability. A dispatch which survives that far and reaches the Lua backend is reported there instead of failing as an unimplemented case. By then it is known to be both reachable and unresolvable, which is exactly when saying so is useful.
1 parent 4124bb8 commit 13eb5f5

3 files changed

Lines changed: 74 additions & 14 deletions

File tree

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

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,17 @@ public void transformGenericNewOnly() {
132132
}
133133

134134
/**
135-
* Deals with the dispatches left after targeted specialization.
135+
* Neutralises the dispatches left in functions that were specialized.
136136
* <p>
137-
* A function that has a specialization is dead: every reachable call to it was rewritten to
138-
* that specialization, so a dispatch still sitting in the original can never run. This backend
139-
* keeps generics rather than removing them wholesale, so those originals are still translated,
140-
* and a dispatch would reach a backend with no way to express it. Such a dispatch is replaced by
141-
* the default value of its type.
137+
* Such a function is dead: every reachable call to it was rewritten to its specialization, so a
138+
* dispatch still sitting in the original can never run. This backend keeps generics rather than
139+
* removing them wholesale, so those originals are still translated and the dispatch would reach
140+
* a backend with no way to express it.
142141
* <p>
143-
* A dispatch in a function that was never specialized is a different matter: it would run, and
144-
* nothing has supplied the concrete type. That is reported rather than quietly defaulted.
142+
* Anything else is left alone. A dispatch may legitimately remain in a bounded generic that is
143+
* merely declared and never called, and garbage removal deletes those later; deciding here
144+
* would mean duplicating reachability. One which survives that far and still reaches the
145+
* backend is reported there, where it is known to be both reachable and unresolvable.
145146
*/
146147
private void settleRemainingDispatches() {
147148
List<ImTypeVarDispatch> remaining = new ArrayList<>();
@@ -156,12 +157,7 @@ public void visit(ImTypeVarDispatch dispatch) {
156157
ImFunction owner = dispatch.getNearestFunc();
157158
if (owner != null && specializedFunctions.containsRow(owner)) {
158159
dispatch.replaceBy(defaultValueFor(dispatch.getTypeClassFunc().getReturnType()));
159-
continue;
160160
}
161-
throw new CompileError(dispatch.attrTrace().attrSource(),
162-
"Type class dispatch of " + dispatch.getTypeClassFunc().getName()
163-
+ " could not be resolved for this target: the concrete type is not available"
164-
+ " where it is used.");
165161
}
166162
}
167163

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/ExprTranslation.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.peeeq.wurstscript.translation.lua.translation;
22

33
import de.peeeq.wurstscript.WurstOperator;
4+
import de.peeeq.wurstscript.attributes.CompileError;
45
import de.peeeq.wurstscript.jassIm.*;
56
import de.peeeq.wurstscript.luaAst.*;
67
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
@@ -461,7 +462,12 @@ public static LuaExpr translate(ImCompiletimeExpr imCompiletimeExpr, LuaTranslat
461462
}
462463

463464
public static LuaExpr translate(ImTypeVarDispatch imTypeVarDispatch, LuaTranslator tr) {
464-
throw new Error("not implemented");
465+
// Reaching the backend means specialization never supplied a concrete type for this
466+
// dispatch and the code is reachable, since unreachable functions have been removed by now.
467+
throw new CompileError(imTypeVarDispatch.attrTrace().attrSource(),
468+
"Type class dispatch of " + imTypeVarDispatch.getTypeClassFunc().getName()
469+
+ " could not be resolved for the Lua target: the concrete type is not available"
470+
+ " where it is used.");
465471
}
466472

467473
public static LuaExpr translate(ImCast imCast, LuaTranslator tr) {

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,64 @@ public void overloadFromLaterBoundStaysAvailable() {
907907
);
908908
}
909909

910+
/**
911+
* A bounded generic which is only declared, never called, stays valid: it is unreachable, so
912+
* nothing has to supply a concrete type for it.
913+
*/
914+
@Test
915+
public void unusedBoundedGenericFunctionLua() {
916+
test().testLua(true).executeProg().lines(
917+
"package test",
918+
"native testSuccess()",
919+
"interface Show<T:>",
920+
" function show(T x) returns string",
921+
"implements Show<int>",
922+
" function show(int x) returns string",
923+
" return \"i\"",
924+
"function unused<Q: Show>(Q x) returns string",
925+
" return Q.show(x)",
926+
"init",
927+
" testSuccess()"
928+
);
929+
}
930+
931+
/** The same for a bounded generic class which is never constructed. */
932+
@Test
933+
public void unusedBoundedGenericClassLua() {
934+
test().testLua(true).executeProg().lines(
935+
"package test",
936+
"native testSuccess()",
937+
"interface Show<T:>",
938+
" function show(T x) returns string",
939+
"implements Show<int>",
940+
" function show(int x) returns string",
941+
" return \"i\"",
942+
"class Unused<Q: Show>",
943+
" function render(Q x) returns string",
944+
" return Q.show(x)",
945+
"init",
946+
" testSuccess()"
947+
);
948+
}
949+
950+
/** Declared and unused on Jass too, which is where it already worked. */
951+
@Test
952+
public void unusedBoundedGenericFunction() {
953+
testAssertOkLines(true,
954+
"package test",
955+
"native testSuccess()",
956+
"interface Show<T:>",
957+
" function show(T x) returns string",
958+
"implements Show<int>",
959+
" function show(int x) returns string",
960+
" return \"i\"",
961+
"function unused<Q: Show>(Q x) returns string",
962+
" return Q.show(x)",
963+
"init",
964+
" testSuccess()"
965+
);
966+
}
967+
910968
/** A type parameter is not a value, so it may only appear as the receiver of a requirement. */
911969
@Test
912970
public void typeParameterIsNotAValue() {

0 commit comments

Comments
 (0)