Skip to content

Commit 2ca5471

Browse files
committed
fix generic override behavior
1 parent 93cad33 commit 2ca5471

3 files changed

Lines changed: 128 additions & 1 deletion

File tree

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,13 @@ public void visit(ImFunctionCall f) {
967967
@Override
968968
public void visit(ImMethodCall mc) {
969969
super.visit(mc);
970-
if (!mc.getTypeArguments().isEmpty()) {
970+
ImMethod method = mc.getMethod();
971+
boolean hasTypeArgs = !mc.getTypeArguments().isEmpty();
972+
// Interface/base dispatch methods can be non-generic but still require specialization
973+
// when they dispatch to generic implementors.
974+
boolean needsDispatchSpecialization =
975+
methodImplementationIsGeneric(method) || hasGenericSubmethodImplementation(method);
976+
if (hasTypeArgs || needsDispatchSpecialization) {
971977
dbg("COLLECT GenericMethodCall: method=" + mc.getMethod().getName() + " " + id(mc.getMethod())
972978
+ " impl=" + (mc.getMethod().getImplementation() == null ? "null" : (mc.getMethod().getImplementation().getName() + " " + id(mc.getMethod().getImplementation())))
973979
+ " owningClass=" + (mc.getMethod().attrClass() == null ? "null" : (mc.getMethod().attrClass().getName() + " " + id(mc.getMethod().attrClass())))
@@ -1109,6 +1115,30 @@ public void visit(ImTypeIdOfClass f) {
11091115
});
11101116
}
11111117

1118+
private boolean methodImplementationIsGeneric(ImMethod method) {
1119+
ImFunction implementation = method.getImplementation();
1120+
return implementation != null && !implementation.getTypeVariables().isEmpty();
1121+
}
1122+
1123+
private boolean hasGenericSubmethodImplementation(ImMethod method) {
1124+
return hasGenericSubmethodImplementation(method, Collections.newSetFromMap(new IdentityHashMap<>()));
1125+
}
1126+
1127+
private boolean hasGenericSubmethodImplementation(ImMethod method, Set<ImMethod> visited) {
1128+
if (!visited.add(method)) {
1129+
return false;
1130+
}
1131+
for (ImMethod subMethod : method.getSubMethods()) {
1132+
if (methodImplementationIsGeneric(subMethod)) {
1133+
return true;
1134+
}
1135+
if (hasGenericSubmethodImplementation(subMethod, visited)) {
1136+
return true;
1137+
}
1138+
}
1139+
return false;
1140+
}
1141+
11121142
static boolean isGenericType(ImType type) {
11131143
return type.match(new ImType.Matcher<Boolean>() {
11141144
@Override

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,11 @@ public void fullArrayListTest() throws IOException {
20932093
assertFalse(compiled.contains("ArrayList_nextFreeIndex_"));
20942094
}
20952095

2096+
@Test
2097+
public void fullReactiveGenericDispatchTest() throws IOException {
2098+
test().withStdLib().executeProg().executeTests().file(new File(TEST_DIR + "reactiveGenericsDispatch.wurst"));
2099+
}
2100+
20962101
@Test
20972102
public void genericNullComparison() {
20982103
testAssertOkLinesWithStdLib(true,
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package ReactiveGenericDispatchRegression
2+
import NoWurst
3+
import Integer
4+
import Wurstunit
5+
6+
interface ReactiveSource
7+
function subscribe(Observer observer)
8+
function unsubscribe(Observer observer)
9+
10+
class Observer
11+
int dependencyChanges = 0
12+
13+
function trackSource(ReactiveSource source)
14+
source.subscribe(this)
15+
16+
function untrackSource(ReactiveSource source)
17+
source.unsubscribe(this)
18+
19+
function onDependencyChanged()
20+
dependencyChanges += 1
21+
22+
23+
class Signal<T:> implements ReactiveSource
24+
private T value
25+
private Observer subscriber = null
26+
27+
construct(T initial)
28+
value = initial
29+
30+
function set(T newValue)
31+
value = newValue
32+
if subscriber != null
33+
subscriber.onDependencyChanged()
34+
35+
function subscriberCount() returns int
36+
return subscriber == null ? 0 : 1
37+
38+
override function subscribe(Observer observer)
39+
subscriber = observer
40+
41+
override function unsubscribe(Observer observer)
42+
if subscriber == observer
43+
subscriber = null
44+
45+
46+
class PlainSource implements ReactiveSource
47+
private Observer subscriber = null
48+
49+
function fire()
50+
if subscriber != null
51+
subscriber.onDependencyChanged()
52+
53+
function subscriberCount() returns int
54+
return subscriber == null ? 0 : 1
55+
56+
override function subscribe(Observer observer)
57+
subscriber = observer
58+
59+
override function unsubscribe(Observer observer)
60+
if subscriber == observer
61+
subscriber = null
62+
63+
init
64+
let genericObserver = new Observer()
65+
let genericSignal = new Signal<int>(0)
66+
genericObserver.trackSource(genericSignal)
67+
if genericSignal.subscriberCount() != 1
68+
testFail("generic subscribe dispatch failed")
69+
genericSignal.set(1)
70+
if genericObserver.dependencyChanges != 1
71+
testFail("generic notify dispatch failed")
72+
genericObserver.untrackSource(genericSignal)
73+
if genericSignal.subscriberCount() != 0
74+
testFail("generic unsubscribe dispatch failed")
75+
76+
let plainObserver = new Observer()
77+
let plainSource = new PlainSource()
78+
plainObserver.trackSource(plainSource)
79+
if plainSource.subscriberCount() != 1
80+
testFail("plain subscribe dispatch failed")
81+
plainSource.fire()
82+
if plainObserver.dependencyChanges != 1
83+
testFail("plain notify dispatch failed")
84+
plainObserver.untrackSource(plainSource)
85+
if plainSource.subscriberCount() != 0
86+
testFail("plain unsubscribe dispatch failed")
87+
88+
destroy genericSignal
89+
destroy genericObserver
90+
destroy plainSource
91+
destroy plainObserver
92+
testSuccess()

0 commit comments

Comments
 (0)