Skip to content

Commit d181a14

Browse files
committed
Track erased generic static ownership
1 parent 214566c commit d181a14

4 files changed

Lines changed: 77 additions & 5 deletions

File tree

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ private void collectGenericNewUse(ImFunctionCall call) {
465465
}
466466
return;
467467
}
468+
recordErasedConstructorAllocation(call);
468469
if (!call.getTypeArguments().isEmpty()
469470
&& (shouldSpecializeTupleArguments(call.getTypeArguments())
470471
|| needsRuntimeTypeSpecialization(call)
@@ -479,6 +480,23 @@ private void collectGenericNewUse(ImFunctionCall call) {
479480
}
480481
}
481482

483+
private void recordErasedConstructorAllocation(ImFunctionCall call) {
484+
if (call.getTypeArguments().isEmpty()
485+
|| typeArgumentsContainTypeVariable(call.getTypeArguments())
486+
|| !(call.getFunc().getTrace() instanceof ConstructorDef)
487+
|| !(call.getFunc().getReturnType() instanceof ImClassType)
488+
|| shouldSpecializeTupleArguments(call.getTypeArguments())
489+
|| needsRuntimeTypeSpecialization(call)) {
490+
return;
491+
}
492+
ImClass owner = classOwning(call.getFunc());
493+
if (owner != null && classOwnsGenericGlobals(owner)
494+
&& !functionNeedsSpecialization(call.getFunc(),
495+
Collections.newSetFromMap(new IdentityHashMap<>()))) {
496+
translator.recordErasedGenericAllocation(owner, call.getTypeArguments());
497+
}
498+
}
499+
482500
/**
483501
* Collects a call which names a function of a generic class outright, taking the instantiation
484502
* from the receiver it was handed.
@@ -571,10 +589,15 @@ private void collectCallThroughGenericReceiver(ImFunctionCall call) {
571589
private void collectGenericNewUse(ImAlloc alloc) {
572590
ImClassType clazz = alloc.getClazz();
573591
if (clazz.getTypeArguments().isEmpty()
574-
|| typeArgumentsContainTypeVariable(clazz.getTypeArguments())
575-
|| (!shouldSpecializeTupleArguments(clazz.getTypeArguments())
576-
&& !needsRuntimeTypeSpecialization(clazz)
577-
&& !isConstructionOnlyInstantiation(clazz.getClassDef()))) {
592+
|| typeArgumentsContainTypeVariable(clazz.getTypeArguments())) {
593+
return;
594+
}
595+
if (!shouldSpecializeTupleArguments(clazz.getTypeArguments())
596+
&& !needsRuntimeTypeSpecialization(clazz)
597+
&& !isConstructionOnlyInstantiation(clazz.getClassDef())) {
598+
if (classOwnsGenericGlobals(clazz.getClassDef())) {
599+
translator.recordErasedGenericAllocation(clazz.getClassDef(), clazz.getTypeArguments());
600+
}
578601
return;
579602
}
580603
genericsUses.add(new GenericClazzUse(alloc));

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public record Specialisation(Element original, List<ImTypeArgument> typeArgument
6161
}
6262

6363
private final Map<Element, Specialisation> specialisations = new IdentityHashMap<>();
64+
private final Map<ImClass, Set<GenericTypes>> erasedGenericAllocations = new IdentityHashMap<>();
6465

6566
/**
6667
* @param typeArguments the arguments the copy was made for, empty when a copy carries none of its
@@ -99,6 +100,31 @@ public void recordGenericStaticOwner(ImVar global, ImClass owner) {
99100
return specialisations.get(copy);
100101
}
101102

103+
public void recordErasedGenericAllocation(ImClass clazz, List<ImTypeArgument> typeArguments) {
104+
erasedGenericAllocations.computeIfAbsent(canonical(clazz), ignored -> new HashSet<>())
105+
.add(new GenericTypes(typeArguments));
106+
}
107+
108+
public boolean hasErasedAllocationWithoutStaticSpecialization(ImClass clazz, ImVar originalStatic) {
109+
Set<GenericTypes> allocations = erasedGenericAllocations.get(canonical(clazz));
110+
if (allocations == null || allocations.isEmpty()) {
111+
return false;
112+
}
113+
Set<GenericTypes> specializedStatics = new HashSet<>();
114+
for (Map.Entry<Element, Specialisation> entry : specialisations.entrySet()) {
115+
Specialisation specialization = entry.getValue();
116+
if (specialization.original() == originalStatic) {
117+
specializedStatics.add(new GenericTypes(specialization.typeArguments()));
118+
}
119+
}
120+
for (GenericTypes allocation : allocations) {
121+
if (!specializedStatics.contains(allocation)) {
122+
return true;
123+
}
124+
}
125+
return false;
126+
}
127+
102128
/**
103129
* The node {@code copy} was ultimately copied from, or {@code copy} itself.
104130
* <p>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ public static void removePhantomGenericStaticInitializers(ImProg prog, ImTransla
190190
changed = false;
191191
for (ImVar original : candidates.keySet()) {
192192
ImClass owner = translator.genericStaticOwnerOf(original);
193-
if ((used.getVars().contains(original) || used.getInstantiatedClasses().contains(owner))
193+
boolean erasedInstantiationNeedsOriginal = used.getInstantiatedClasses().contains(owner)
194+
&& translator.hasErasedAllocationWithoutStaticSpecialization(owner, original);
195+
if ((used.getVars().contains(original) || erasedInstantiationNeedsOriginal)
194196
&& liveOriginals.add(original)) {
195197
changed = true;
196198
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,27 @@ public void genericStaticsAreIndependentWithoutTupleInstantiation() throws IOExc
10081008
2, storages);
10091009
}
10101010

1011+
@Test
1012+
public void constructedErasedInstantiationDoesNotDuplicateSpecializedStaticInitializer() {
1013+
test().testLua(true).executeProg().lines(
1014+
"package Test",
1015+
"native testSuccess()",
1016+
"int bumps",
1017+
"function bump() returns int",
1018+
" bumps++",
1019+
" return bumps",
1020+
"class Box<T:>",
1021+
" static int value = bump()",
1022+
" construct()",
1023+
" static function get() returns int",
1024+
" return value",
1025+
"init",
1026+
" new Box<int>()",
1027+
" if Box<int>.get() == 1 and bumps == 1",
1028+
" testSuccess()"
1029+
);
1030+
}
1031+
10111032
@Test
10121033
public void randomizedNestedGenericTupleClassShapesMatchAllBackends() {
10131034
record Shape(String type, String value, int constructions) {}

0 commit comments

Comments
 (0)