Skip to content

Commit 1d088f6

Browse files
authored
Fix new generics with spefic modules (#1122), fixes #886
1 parent 546549f commit 1d088f6

2 files changed

Lines changed: 143 additions & 12 deletions

File tree

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

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public class EliminateGenerics {
2424
private final Table<ImClass, GenericTypes, ImClass> specializedClasses = HashBasedTable.create();
2525
private final Multimap<ImClass, BiConsumer<GenericTypes, ImClass>> onSpecializedClassTriggers = HashMultimap.create();
2626

27+
// Track concrete generic arguments for specialized functions to simplify later lookups
28+
private final Map<ImFunction, GenericTypes> specializedFunctionGenerics = new IdentityHashMap<>();
29+
2730
// NEW: Track specialized global variables for generic static fields
2831
// Key: (original generic global var, concrete type instantiation) -> specialized var
2932
private final Table<ImVar, GenericTypes, ImVar> specializedGlobals = HashBasedTable.create();
@@ -205,15 +208,6 @@ private void removeGenericConstructs() {
205208
for (ImClass c : prog.getClasses()) {
206209
c.getFields().removeIf(f -> isGenericType(f.getType()));
207210
}
208-
209-
// NEW: Remove original generic global variables
210-
prog.getGlobals().removeIf(v -> {
211-
if (globalToClass.containsKey(v)) {
212-
WLogger.info("Removing generic global variable: " + v.getName() + " with type " + v.getType());
213-
return true;
214-
}
215-
return false;
216-
});
217211
}
218212

219213
private void eliminateGenericUses() {
@@ -283,6 +277,7 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) {
283277

284278
ImFunction newF = f.copyWithRefs();
285279
specializedFunctions.put(f, generics, newF);
280+
specializedFunctionGenerics.put(newF, generics);
286281
prog.getFunctions().add(newF);
287282
newF.getTypeVariables().removeAll();
288283
List<ImTypeVar> typeVars = f.getTypeVariables();
@@ -442,7 +437,7 @@ private ImClass specializeClass(ImClass c, GenericTypes generics) {
442437
return specialized;
443438
}
444439
if (generics.containsTypeVariable()) {
445-
throw new CompileError(c, "Generics should not contain type variables.");
440+
throw new CompileError(c, "Generics should not contain type variables (" + c.getName() + " ⟪" + generics.makeName() + "⟫).");
446441
}
447442
ImClass newC = c.copyWithRefs();
448443
newC.setSuperClasses(new ArrayList<>(newC.getSuperClasses()));
@@ -816,6 +811,22 @@ public void eliminate() {
816811
ImVar f = ma.getVar();
817812
ImClass owningClass = (ImClass) f.getParent().getParent();
818813
GenericTypes generics = new GenericTypes(specializeTypeArgs(ma.getTypeArguments()));
814+
// If the access still carries type variables, defer specialization until a concrete
815+
// instantiation is created (e.g. when the surrounding generic function/class is
816+
// specialized). If the receiver type is already concrete we can directly resolve the
817+
// target field using that type information.
818+
if (generics.containsTypeVariable()) {
819+
ImType receiverType = specializeType(ma.getReceiver().attrTyp());
820+
if (receiverType instanceof ImClassType) {
821+
ImClass specializedClass = ((ImClassType) receiverType).getClassDef();
822+
int fieldIndex = owningClass.getFields().indexOf(f);
823+
ImVar newVar = specializedClass.getFields().get(fieldIndex);
824+
ma.setVar(newVar);
825+
ma.getTypeArguments().removeAll();
826+
newVar.setType(specializeType(newVar.getType()));
827+
}
828+
return;
829+
}
819830
ImClass specializedClass = specializeClass(owningClass, generics);
820831
int fieldIndex = owningClass.getFields().indexOf(f);
821832
ImVar newVar = specializedClass.getFields().get(fieldIndex);
@@ -925,6 +936,11 @@ private GenericTypes inferGenericsFromFunction(Element element, ImClass owningCl
925936
if (current instanceof ImFunction) {
926937
ImFunction func = (ImFunction) current;
927938

939+
GenericTypes specialized = specializedFunctionGenerics.get(func);
940+
if (specialized != null) {
941+
return specialized;
942+
}
943+
928944
// If function is still generic, we can't decide yet.
929945
if (!func.getTypeVariables().isEmpty()) {
930946
return null;
@@ -1073,7 +1089,22 @@ private ImType specializeType(ImType type) {
10731089
public ImType case_ImClassType(ImClassType t) {
10741090
ImTypeArguments typeArgs = t.getTypeArguments();
10751091
List<ImTypeArgument> newTypeArgs = specializeTypeArgs(typeArgs);
1076-
ImClass specializedClass = specializeClass(t.getClassDef(), new GenericTypes(newTypeArgs));
1092+
GenericTypes generics = new GenericTypes(newTypeArgs);
1093+
1094+
if (generics.containsTypeVariable()) {
1095+
Map<GenericTypes, ImClass> specialized = specializedClasses.row(t.getClassDef());
1096+
1097+
if (!specialized.isEmpty()) {
1098+
ImClass firstSpecialization = specialized.values().iterator().next();
1099+
return JassIm.ImClassType(firstSpecialization, JassIm.ImTypeArguments());
1100+
}
1101+
1102+
ImTypeArguments copiedArgs = JassIm.ImTypeArguments();
1103+
copiedArgs.addAll(newTypeArgs);
1104+
return JassIm.ImClassType(t.getClassDef(), copiedArgs);
1105+
}
1106+
1107+
ImClass specializedClass = specializeClass(t.getClassDef(), generics);
10771108
return JassIm.ImClassType(specializedClass, JassIm.ImTypeArguments());
10781109
}
10791110

@@ -1099,7 +1130,27 @@ class GenericReturnTypeFunc implements GenericUse {
10991130

11001131
@Override
11011132
public void eliminate() {
1102-
mc.setReturnType(specializeType(mc.getReturnType()));
1133+
ImType returnType = mc.getReturnType();
1134+
1135+
if (containsTypeVariable(returnType) && returnType instanceof ImClassType && !mc.getParameters().isEmpty()) {
1136+
ImClassType retClassType = (ImClassType) returnType;
1137+
ImType receiverType = mc.getParameters().get(0).getType();
1138+
1139+
if (receiverType instanceof ImClassType) {
1140+
ImClassType receiverClassType = (ImClassType) receiverType;
1141+
ImClassType adapted = adaptToSuperclass(receiverClassType, retClassType.getClassDef());
1142+
1143+
if (adapted != null) {
1144+
GenericTypes concrete = new GenericTypes(specializeTypeArgs(adapted.getTypeArguments()));
1145+
ImType specialized = ImAttrType.substituteType(returnType, concrete.getTypeArguments(), retClassType.getClassDef().getTypeVariables());
1146+
1147+
mc.setReturnType(specializeType(specialized));
1148+
return;
1149+
}
1150+
}
1151+
}
1152+
1153+
mc.setReturnType(specializeType(returnType));
11031154
}
11041155
}
11051156

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,5 +1843,85 @@ public void arrayListInClosure() {
18431843
);
18441844
}
18451845

1846+
@Test
1847+
public void linkedListModule() {
1848+
testAssertOkLines(true,
1849+
"package test",
1850+
"native testSuccess()",
1851+
"module LinkedListModule",
1852+
" static thistype first = null",
1853+
" static thistype last = null",
1854+
" static int size = 0",
1855+
" thistype prev",
1856+
" thistype next",
1857+
" construct()",
1858+
" size++",
1859+
" if size == 1",
1860+
" first = this",
1861+
" prev = null",
1862+
" else",
1863+
" prev = last",
1864+
" last.next = this",
1865+
" first.prev = this",
1866+
" next = null",
1867+
" last = this",
1868+
" static function getFirst() returns thistype",
1869+
" return first",
1870+
" function getNext() returns thistype",
1871+
" if next == null",
1872+
" return first",
1873+
" return next",
1874+
" function getPrev() returns thistype",
1875+
" if prev == null",
1876+
" return last",
1877+
" return prev",
1878+
" function remove()",
1879+
" size--",
1880+
" if this != first",
1881+
" prev.next = next",
1882+
" else",
1883+
" first = next",
1884+
" if this != last",
1885+
" next.prev = prev",
1886+
" else",
1887+
" last = prev",
1888+
" ondestroy",
1889+
" remove()",
1890+
"class Node<T:>",
1891+
" use LinkedListModule",
1892+
"init",
1893+
" let a = new Node<int>",
1894+
" let b = new Node<int>",
1895+
" let c = new Node<int>",
1896+
" // simple sanity check: circular next traversal should loop",
1897+
" if a.getNext() != null and a.getPrev() != null",
1898+
" testSuccess()",
1899+
"endpackage"
1900+
);
1901+
}
1902+
1903+
1904+
@Test
1905+
public void genericClassWithLLModule() {
1906+
testAssertOkLinesWithStdLib(true,
1907+
"package test",
1908+
"import LinkedListModule",
1909+
"class Box<T:>",
1910+
" use LinkedListModule",
1911+
" private T value",
1912+
" function setValue(T v)",
1913+
" value = v",
1914+
" function getValue() returns T",
1915+
" return value",
1916+
"init",
1917+
" let b = new Box<int>",
1918+
" b.setValue(42)",
1919+
" if b.getValue() == 42 and b.prev == null",
1920+
" testSuccess()",
1921+
"endpackage"
1922+
);
1923+
}
1924+
1925+
18461926

18471927
}

0 commit comments

Comments
 (0)