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 @@ -1278,7 +1278,9 @@ private void removeNonSpecializedGlobals() {
List<ImSet> inits = prog.getGlobalInits().remove(imVar);
if (inits != null) {
for (ImSet init : inits) {
init.replaceBy(ImHelper.nullExpr());
if (init.getParent() != null) {
init.replaceBy(ImHelper.nullExpr());
}
}
}
}
Expand Down Expand Up @@ -2243,18 +2245,29 @@ private void createSpecializedGlobals(ImClass originalClass, GenericTypes generi
List<ImSet> originalInits = prog.getGlobalInits().get(originalGlobal);
if (originalInits != null && !originalInits.isEmpty()) {

ImSet firstOrig = originalInits.getFirst();
if (!(firstOrig.getParent() instanceof ImStmts parentStmts)) {
throw new CompileError(originalGlobal,
"Initializer for global " + originalGlobal.getName() + " is not inside ImStmts.");
}
// ensure all original init sets share the same parent statement list
ImStmts parentStmts = null;
boolean hasDetachedInits = false;
for (ImSet s : originalInits) {
if (s.getParent() != parentStmts) {
if (s.getParent() == null) {
hasDetachedInits = true;
continue;
}
if (!(s.getParent() instanceof ImStmts)) {
throw new CompileError(originalGlobal,
"Initializer for global " + originalGlobal.getName() + " is not inside ImStmts.");
}
ImStmts currParent = (ImStmts) s.getParent();
if (parentStmts == null) {
parentStmts = currParent;
} else if (parentStmts != currParent) {
throw new CompileError(originalGlobal,
"Initializer statements for global " + originalGlobal.getName() + " are not in the same ImStmts.");
}
}
if (hasDetachedInits && parentStmts != null) {
throw new CompileError(originalGlobal,
"Initializer statements for global " + originalGlobal.getName() + " are inconsistently attached.");
}

// Helper: rebuild LHS as ImLExpr for specialized global
java.util.function.Function<ImLExpr, ImLExpr> specializeLhs = (ImLExpr lhs) -> {
Expand Down Expand Up @@ -2292,11 +2305,13 @@ private void createSpecializedGlobals(ImClass originalClass, GenericTypes generi
// Append after earlier specializations of this initializer. Each invocation of
// createSpecializedGlobals has its own insertion batch; always inserting after
// origSet would therefore reverse specialization discovery/initializer order.
ImStmt insertionPoint = specializedInitializerTails.getOrDefault(origSet, origSet);
IdentityHashMap<ImStmt, List<ImStmt>> byStmt =
insertsByParent.computeIfAbsent(parentStmts, k -> new IdentityHashMap<>());
byStmt.computeIfAbsent(insertionPoint, k -> new ArrayList<>(1)).add(specSet);
specializedInitializerTails.put(origSet, specSet);
if (parentStmts != null) {
ImStmt insertionPoint = specializedInitializerTails.getOrDefault(origSet, origSet);
IdentityHashMap<ImStmt, List<ImStmt>> byStmt =
insertsByParent.computeIfAbsent(parentStmts, k -> new IdentityHashMap<>());
byStmt.computeIfAbsent(insertionPoint, k -> new ArrayList<>(1)).add(specSet);
specializedInitializerTails.put(origSet, specSet);
}

// keep prog.getGlobalInits consistent, but do NOT reuse the tree-attached node elsewhere
specializedInitsForMap.add((ImSet) specSet.copy());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ public static void removePhantomGenericStaticInitializers(ImProg prog, ImTransla
}
prog.getGlobalInits().remove(candidate.getKey());
for (ImSet initializer : candidate.getValue()) {
if (initializer.getParent() == null) {
continue;
}
if (!(initializer.getParent() instanceof ImStmts statements)) {
throw new IllegalStateException("Global initializer is not attached to an ImStmts node.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
import de.peeeq.wurstscript.RunArgs;
import de.peeeq.wurstscript.ast.WurstModel;
import de.peeeq.wurstscript.gui.WurstGuiCliImpl;
import de.peeeq.wurstscript.jassIm.ImProg;
import de.peeeq.wurstscript.jassIm.ImSet;
import de.peeeq.wurstscript.jassIm.ImVar;
import de.peeeq.wurstscript.jassIm.JassIm;
import de.peeeq.wurstscript.luaAst.LuaCompilationUnit;
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
import org.wurstscript.projectconfig.WurstProjectConfigData;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -1010,6 +1015,56 @@ public void genericStaticsAreIndependentWithoutTupleInstantiation() throws IOExc
2, storages);
}

@Test
public void detachedGenericStaticInitializerRemainsMetadataOnly() {
RunArgs runArgs = new RunArgs().with("-lua");
WurstGuiCliImpl gui = new WurstGuiCliImpl();
WurstCompilerJassImpl compiler = new WurstCompilerJassImpl(null, gui, null, runArgs);
WurstModel model = parseFiles(Collections.emptyList(), Collections.singletonList(new CU(
"detachedGenericStaticInitializerRemainsMetadataOnly.wurst", String.join("\n",
"package Test",
"native testSuccess()",
"class Slot<T:>",
" static T value",
" static function set(T newValue)",
" value = newValue",
" static function get() returns T",
" return value",
"init",
" Slot<int>.set(7)",
" Slot<string>.set(\"ok\")",
" if Slot<int>.get() == 7 and Slot<string>.get() == \"ok\"",
" testSuccess()"
))), false, compiler);
assertTrue("unexpected parse/type errors: " + gui.getErrorList(), gui.getErrorList().isEmpty());
compiler.checkProg(model);
assertTrue("unexpected compile errors: " + gui.getErrorList(), gui.getErrorList().isEmpty());
ImProg prog = compiler.translateProgToIm(model);
compiler.runCompiletime(WurstProjectConfigData.empty(), false, false);

ImVar genericStatic = prog.getGlobals().stream()
.filter(global -> global.getName().contains("value"))
.findFirst()
.orElseThrow(() -> new AssertionError("expected the translated Slot.value global"));
ImSet detached = JassIm.ImSet(genericStatic.attrTrace(), JassIm.ImVarAccess(genericStatic),
ImHelper.nullExpr());
prog.getGlobalInits().put(genericStatic, Collections.singletonList(detached));
assertTrue("default initializer must remain detached metadata", detached.getParent() == null);

LuaCompilationUnit luaCode = compiler.transformProgToLua();
StringBuilder output = new StringBuilder();
luaCode.print(output, 0);
java.util.regex.Matcher declarations = java.util.regex.Pattern
.compile("(?m)^Slot_value_\\S* = nil$")
.matcher(output);
int storages = 0;
while (declarations.find()) {
storages++;
}
assertEquals("both concrete static specializations must survive detached initialization",
2, storages);
}

@Test
public void constructedErasedInstantiationDoesNotDuplicateSpecializedStaticInitializer() {
test().testLua(true).executeProg().lines(
Expand Down
Loading