Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1971,7 +1971,7 @@ private void adaptSubmethods(List<ImMethod> oldSubMethods, ImMethod newM, Generi
}
});
}else {
subClass.getSuperClasses().replaceAll(this::specializeType);
specializeSuperClasses(subClass);
ImClassType newClassTspecialized = specializeType(newClassT);
if (subClass.isSubclassOf(newClassTspecialized.getClassDef())) {
newM.getSubMethods().add(subMethod);
Expand All @@ -1980,6 +1980,23 @@ private void adaptSubmethods(List<ImMethod> oldSubMethods, ImMethod newM, Generi
}
}

/**
* Replaces the superclasses of a class with their specialisations.
* <p>
* Specialising a superclass can specialise its class. That runs the triggers registered on that
* class, which specialise its methods and adapt their submethods, and so come back here, for this
* class too; and {@link #rewriteRuntimeTypeSuperEdges} rewrites the superclasses of every class.
* The list can therefore change while this runs: it is read from a copy and replaced, never
* rewritten in place.
*/
private void specializeSuperClasses(ImClass c) {
List<ImClassType> specialized = new ArrayList<>();
for (ImClassType superClass : new ArrayList<>(c.getSuperClasses())) {
specialized.add(specializeType(superClass));
}
c.setSuperClasses(specialized);
}

/**
* Replaces all uses of the given typeVars with the type arguments given in parameter generics.
*/
Expand Down Expand Up @@ -2238,7 +2255,7 @@ private ImClass specializeClass(ImClass c, GenericTypes generics) {
: c.getName() + "⟪" + generics.makeName() + "⟫");
List<ImTypeVar> typeVars = c.getTypeVariables();
rewriteGenerics(newC, generics, typeVars);
newC.getSuperClasses().replaceAll(this::specializeType);
specializeSuperClasses(newC);
if (needsRuntimeTypeSpecialization(c, generics, new HashSet<>())) {
rewriteRuntimeTypeSuperEdges(c, generics, newC);
}
Expand Down Expand Up @@ -2632,14 +2649,7 @@ public void visit(ImClass c) {
// handle generic classes after they are specialized
return;
}
genericsUses.add(() -> {
List<ImClassType> newSuperClasses = new ArrayList<>();
for (ImClassType imClassType : c.getSuperClasses()) {
ImClassType specializeType = EliminateGenerics.this.specializeType(imClassType);
newSuperClasses.add(specializeType);
}
c.setSuperClasses(newSuperClasses);
});
genericsUses.add(() -> specializeSuperClasses(c));

super.visit(c);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ private void translateInterfaceFuncDef(FuncDef f) {
* are Base's ({@link ImTranslator#linkOverridesBelow}). Where another interface of C gives m a default, the
* default is the implementation: a default beats an inherited method (a call through that interface runs it on
* every backend), and Lua binds one implementation for C to both. Not for generic classes: a method of a generic
* class is specialised with the functions the class owns, and a generic class with an override in a non-generic
* subclass does not compile yet (EliminateGenerics.adaptSubmethods), so those keep what they did.
* class is specialised with the functions the class owns, so those keep what they did.
*/
private ImMethod methodOfItsOwn(ImClass imClass, ImMethod inherited, ImFunction implementation) {
ImMethod own = JassIm.ImMethod(inherited.getTrace(), translator.selfType(imClass), inherited.getName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2460,4 +2460,51 @@ public void genericStaticInit() throws IOException {
assertTrue(jass.contains("set Box_cap_Box_real__u = 16"));
}

@Test
public void genericOverrideOverriddenAgainByPlainSubclass() {
test().testLua(true).luaOnly(false).executeProg().lines(
"package test",
"native testSuccess()",
"class Base",
" function m() returns int",
" return 1",
"class C<T:> extends Base",
" override function m() returns int",
" return 2",
"class D extends C<int>",
" override function m() returns int",
" return 4",
"function viaBase(Base b) returns int",
" return b.m()",
"init",
" if viaBase(new Base()) == 1 and viaBase(new C<int>()) == 2 and viaBase(new D()) == 4",
" testSuccess()");
}

@Test
public void genericOverrideOverriddenAgainBySubclassWithInterface() {
test().testLua(true).luaOnly(false).executeProg().lines(
"package test",
"native testSuccess()",
"interface I",
" function n() returns int",
"class Base",
" function m() returns int",
" return 1",
"class C<T:> extends Base",
" override function m() returns int",
" return 2",
"class D extends C<int> implements I",
" override function m() returns int",
" return 4",
" function n() returns int",
" return 8",
"function viaBase(Base b) returns int",
" return b.m()",
"init",
" let d = new D()",
" if viaBase(new Base()) == 1 and viaBase(new C<int>()) == 2 and viaBase(d) == 4 and d.n() == 8",
" testSuccess()");
}

}
Loading