Skip to content

Commit da1cf37

Browse files
authored
Make compiletime state migration opt-in per global (#1256)
1 parent 85b895f commit da1cf37

4 files changed

Lines changed: 96 additions & 41 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import de.peeeq.wurstio.mpq.MpqEditor;
1414
import de.peeeq.wurstscript.WLogger;
1515
import de.peeeq.wurstscript.ast.Element;
16+
import de.peeeq.wurstscript.ast.GlobalVarDef;
1617
import de.peeeq.wurstscript.attributes.CompileError;
1718
import de.peeeq.wurstscript.attributes.ErrorHandler;
1819
import de.peeeq.wurstscript.gui.WurstGui;
@@ -750,7 +751,8 @@ private void emitCompiletimeState() {
750751
.thenComparing(ImVar::getName);
751752
modifiedScalars.sort(stableGlobalOrder);
752753
for (ImVar var : modifiedScalars) {
753-
if (!imProg.getGlobals().contains(var) || var.getType() instanceof ImArrayLikeType) {
754+
if (!imProg.getGlobals().contains(var) || var.getType() instanceof ImArrayLikeType
755+
|| !isCompiletimeStateMigrationTarget(var)) {
754756
continue;
755757
}
756758
StateReplayLocation replayLocation = findReplayTarget(var);
@@ -779,7 +781,7 @@ private void emitCompiletimeState() {
779781
}
780782
modifiedArrays.sort(stableGlobalOrder);
781783
for (ImVar var : modifiedArrays) {
782-
if (!imProg.getGlobals().contains(var)) {
784+
if (!imProg.getGlobals().contains(var) || !isCompiletimeStateMigrationTarget(var)) {
783785
continue;
784786
}
785787
if (!(var.getType() instanceof ImArrayLikeType)) {
@@ -811,6 +813,13 @@ private void emitCompiletimeState() {
811813
}
812814
}
813815

816+
private boolean isCompiletimeStateMigrationTarget(ImVar var) {
817+
// Compiletime state is deliberately opt-in per global. This keeps
818+
// compiletime-only scratch values and incidental writes out of runtime init.
819+
return var.getTrace() instanceof GlobalVarDef
820+
&& ((GlobalVarDef) var.getTrace()).hasAnnotation("@compiletime");
821+
}
822+
814823
private String sourceDiagnostic(ImVar var) {
815824
return var.getTrace().attrSource().printShort();
816825
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3000,9 +3000,9 @@ public void case_GlobalVarDef(GlobalVarDef g) {
30003000
} else {
30013001
check(VisibilityPublic.class, ModConstant.class, ModReadonly.class, Annotation.class);
30023002
}
3003-
if (g.hasAnnotation("@compiletime")) {
3003+
if (g.attrIsDynamicClassMember() && g.hasAnnotation("@compiletime")) {
30043004
g.getAnnotation("@compiletime")
3005-
.addWarning("The annotation '@compiletime' has no effect on variables.");
3005+
.addWarning("The annotation '@compiletime' has no effect on instance fields.");
30063006
}
30073007
}
30083008

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

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414

1515
public class CompiletimeTests extends WurstScriptTest {
1616

17+
@Test
18+
public void compiletimeAnnotationWarnsOnInstanceFields() {
19+
test().expectWarning("has no effect on instance fields")
20+
.lines("package Test",
21+
"class C",
22+
" @compiletime int value");
23+
}
1724

1825
@Test
1926
public void testSimpleCompiletime() {
@@ -81,29 +88,32 @@ public void testUnsupportedCompiletimeArrayWarningIsAggregatedAndReadable() {
8188
try {
8289
test().withStdLib().testLua(true).luaOnly(true).runCompiletimeFunctions(true)
8390
.lines("package Test",
91+
"@compiletime player array source",
92+
"@compiletime function fill()",
93+
" source[0] = Player(0)",
8494
"init",
85-
" let _firstPlayer = players[0]");
95+
" source[0] = Player(0)");
8696
} finally {
8797
logger.detachAppender(appender);
8898
appender.stop();
8999
}
90100

91101
List<String> playerWarnings = appender.list.stream()
92102
.map(ILoggingEvent::getFormattedMessage)
93-
.filter(message -> message.contains("Player_players"))
103+
.filter(message -> message.contains("Test_source"))
94104
.toList();
95105
assertEquals(playerWarnings.size(), 1, "expected one warning for the entire array");
96106
String warning = playerWarnings.get(0);
97-
assertTrue(warning.contains("28 unsupported compiletime entries"), warning);
98-
assertTrue(warning.contains("Player, line"), warning);
107+
assertTrue(warning.contains("1 unsupported compiletime entries"), warning);
108+
assertTrue(warning.contains("IlConstHandle"), warning);
99109
assertFalse(warning.contains("GlobalVarDef"), warning);
100110
}
101111

102112
@Test
103113
public void testCompiletimePackageScalarState() {
104114
test().testLua(true).luaOnly(false).executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
105115
.lines("package A",
106-
"public int source = 1",
116+
"@compiletime public int source = 1",
107117
"@compiletime function fill()",
108118
" source = 42",
109119
"endpackage",
@@ -123,8 +133,8 @@ public void testCompiletimeObjectAndNullScalarState() {
123133
"native testSuccess()",
124134
"class A",
125135
" int value",
126-
"A source",
127-
"string cleared = \"value\"",
136+
"@compiletime A source",
137+
"@compiletime string cleared = \"value\"",
128138
"@compiletime function fill()",
129139
" source = new A",
130140
" source.value = 42",
@@ -146,7 +156,7 @@ public void testCompiletimeScalarReplayOnlyWrittenValues() {
146156
"import A",
147157
"native testSuccess()",
148158
"int observed = seed",
149-
"int migrated = 0",
159+
"@compiletime int migrated = 0",
150160
"@compiletime function fill()",
151161
" let snapshot = observed",
152162
" migrated = snapshot + 41",
@@ -165,7 +175,7 @@ public void testLazyScalarInitializerSideEffectsAreNotReplayed() {
165175
" counter++",
166176
" return counter",
167177
"int observed = bump()",
168-
"int migrated",
178+
"@compiletime int migrated",
169179
"@compiletime function fill()",
170180
" let _snapshot = observed",
171181
" migrated = 42",
@@ -179,7 +189,7 @@ public void testCompiletimeScalarRuntimeWriteRemainsAuthoritative() {
179189
test().testLua(true).luaOnly(false).executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
180190
.lines("package Test",
181191
"native testSuccess()",
182-
"int source = 1",
192+
"@compiletime int source = 1",
183193
"@compiletime function fill()",
184194
" source = 42",
185195
"init",
@@ -194,7 +204,7 @@ public void testCompiletimeClassStaticScalarState() {
194204
.lines("package Test",
195205
"native testSuccess()",
196206
"class Counter",
197-
" static int value = 1",
207+
" @compiletime static int value = 1",
198208
" static function setValue(int newValue)",
199209
" value = newValue",
200210
" static function getValue() returns int",
@@ -213,7 +223,7 @@ public void testCompiletimeGenericClassStaticScalarState() {
213223
.lines("package Test",
214224
"native testSuccess()",
215225
"class Counter<T:>",
216-
" static T value",
226+
" @compiletime static T value",
217227
" static function setValue(T newValue)",
218228
" value = newValue",
219229
" static function getValue() returns T",
@@ -230,7 +240,7 @@ public void testCompiletimeArrayState() {
230240
test().executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
231241
.lines("package Test",
232242
"native testSuccess()",
233-
"int array source",
243+
"@compiletime int array source",
234244
"@compiletime function fill()",
235245
" source[0] = 42",
236246
"init",
@@ -243,7 +253,7 @@ public void testCompiletimeArrayStateAfterSourceInitializer() {
243253
test().executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
244254
.lines("package Test",
245255
"native testSuccess()",
246-
"int array source = [1]",
256+
"@compiletime int array source = [1]",
247257
"@compiletime function fill()",
248258
" source[0] = 42",
249259
"init",
@@ -256,7 +266,7 @@ public void testCompiletimeArrayStateLua() {
256266
test().testLua(true).executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
257267
.lines("package Test",
258268
"native testSuccess()",
259-
"int array source = [1]",
269+
"@compiletime int array source = [1]",
260270
"@compiletime function fill()",
261271
" source[0] = 42",
262272
"init",
@@ -270,7 +280,7 @@ public void testCompiletimeGenericArrayState() {
270280
.lines("package Test",
271281
"native testSuccess()",
272282
"class Box<T:>",
273-
" static T array store",
283+
" @compiletime static T array store",
274284
" static function set(int index, T value)",
275285
" store[index] = value",
276286
" static function get(int index) returns T",
@@ -288,7 +298,7 @@ public void testCompiletimeGenericArrayStateLua() {
288298
.lines("package Test",
289299
"native testSuccess()",
290300
"class Box<T:>",
291-
" static T array store",
301+
" @compiletime static T array store",
292302
" static function set(int index, T value)",
293303
" store[index] = value",
294304
" static function get(int index) returns T",
@@ -307,7 +317,7 @@ public void testCompiletimeObjectArrayState() {
307317
"native testSuccess()",
308318
"class A",
309319
" int value",
310-
"A array source",
320+
"@compiletime A array source",
311321
"@compiletime function fill()",
312322
" source[0] = new A",
313323
" source[0].value = 42",
@@ -326,7 +336,7 @@ public void testCompiletimeHashtableArrayState() {
326336
"@extern native InitHashtable() returns hashtable",
327337
"@extern native LoadInteger(hashtable h, int p, int c) returns int",
328338
"@extern native SaveInteger(hashtable h, int p, int c, int i)",
329-
"hashtable array source",
339+
"@compiletime hashtable array source",
330340
"@compiletime function fill()",
331341
" source[0] = InitHashtable()",
332342
" SaveInteger(source[0], 2, 3, 42)",
@@ -340,7 +350,7 @@ public void testCompiletimeNullArrayState() {
340350
test().executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
341351
.lines("package Test",
342352
"native testSuccess()",
343-
"string array source = [\"value\"]",
353+
"@compiletime string array source = [\"value\"]",
344354
"@compiletime function clear()",
345355
" source[0] = null",
346356
"init",
@@ -354,7 +364,7 @@ public void testCompiletimeTupleArrayState() {
354364
.lines("package Test",
355365
"native testSuccess()",
356366
"tuple pair(int left, int right)",
357-
"pair array source",
367+
"@compiletime pair array source",
358368
"@compiletime function fill()",
359369
" source[0] = pair(42, 7)",
360370
"init",
@@ -366,7 +376,7 @@ public void testCompiletimeTupleArrayState() {
366376
public void testCompiletimeArrayStateAcrossPackages() {
367377
test().executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
368378
.lines("package A",
369-
"public int array source = [1]",
379+
"@compiletime public int array source = [1]",
370380
"@compiletime function fillA()",
371381
" source[0] = 42",
372382
"init",
@@ -384,15 +394,15 @@ public void testCompiletimeArrayStateAcrossPackages() {
384394
public void testCompiletimeArrayStateAcrossPackagesWithTwoInitializers() {
385395
test().executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
386396
.lines("package A",
387-
"public int array source = [1]",
397+
"@compiletime public int array source = [1]",
388398
"@compiletime function fillA()",
389399
" source[0] = 42",
390400
"init",
391401
" source[0] = 7",
392402
"endpackage",
393403
"package B",
394404
"import A",
395-
"int array other = [2]",
405+
"@compiletime int array other = [2]",
396406
"@compiletime function fillB()",
397407
" other[0] = 9",
398408
"native testSuccess()",
@@ -407,9 +417,9 @@ public void testCompiletimeArrayReplayPrecedesDependentInitializer() {
407417
test().testLua(true).executeProg(true).executeProgOnlyAfterTransforms().runCompiletimeFunctions(true)
408418
.lines("package Test",
409419
"native testSuccess()",
410-
"int array first = [1]",
420+
"@compiletime int array first = [1]",
411421
"int observed = first[0]",
412-
"int array second = [2]",
422+
"@compiletime int array second = [2]",
413423
"@compiletime function fill()",
414424
" first[0] = 42",
415425
" second[0] = 9",
@@ -429,7 +439,7 @@ public void testCompiletimeArrayReplayOnlyWrittenEntries() {
429439
"package B",
430440
"import A",
431441
"native testSuccess()",
432-
"int array source = [seed, 0]",
442+
"@compiletime int array source = [seed, 0]",
433443
"@compiletime function fill()",
434444
" source[1] = 42",
435445
"init",

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

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public void compiletimeGenericArrayReplayLeavesAreSplit() {
6060
new RunArgs().with("-lua", "-runcompiletimefunctions", "-functionSplitLimit", "1"),
6161
"package Test",
6262
"class Box<T:>",
63-
" static T array store",
63+
" @compiletime static T array store",
6464
" static function set(int index, T value)",
6565
" store[index] = value",
6666
" static function get(int index) returns T",
@@ -93,10 +93,10 @@ public void compiletimeArrayReplaySplittingIsDeterministicAcrossPackages() {
9393
RunArgs runArgs = new RunArgs().with(
9494
"-lua", "-runcompiletimefunctions", "-functionSplitLimit", "1");
9595
String[] source = {
96-
"package A", "public int array a = [1]", "@compiletime function fillA()", " a[0] = 10", "endpackage",
97-
"package B", "public int array b = [1]", "@compiletime function fillB()", " b[0] = 20", "endpackage",
98-
"package C", "public int array c = [1]", "@compiletime function fillC()", " c[0] = 30", "endpackage",
99-
"package D", "public int array d = [1]", "@compiletime function fillD()", " d[0] = 40", "endpackage",
96+
"package A", "@compiletime public int array a = [1]", "@compiletime function fillA()", " a[0] = 10", "endpackage",
97+
"package B", "@compiletime public int array b = [1]", "@compiletime function fillB()", " b[0] = 20", "endpackage",
98+
"package C", "@compiletime public int array c = [1]", "@compiletime function fillC()", " c[0] = 30", "endpackage",
99+
"package D", "@compiletime public int array d = [1]", "@compiletime function fillD()", " d[0] = 40", "endpackage",
100100
"package Test", "import A", "import B", "import C", "import D", "native testSuccess()", "init",
101101
" if a[0] + b[0] + c[0] + d[0] == 100", " testSuccess()"
102102
};
@@ -111,10 +111,10 @@ public void compiletimeScalarReplaySplittingIsDeterministicAcrossPackages() {
111111
RunArgs runArgs = new RunArgs().with(
112112
"-lua", "-runcompiletimefunctions", "-functionSplitLimit", "1");
113113
String[] source = {
114-
"package A", "public int a", "@compiletime function fillA()", " a = 10", "endpackage",
115-
"package B", "public int b", "@compiletime function fillB()", " b = 20", "endpackage",
116-
"package C", "public int c", "@compiletime function fillC()", " c = 30", "endpackage",
117-
"package D", "public int d", "@compiletime function fillD()", " d = 40", "endpackage",
114+
"package A", "@compiletime public int a", "@compiletime function fillA()", " a = 10", "endpackage",
115+
"package B", "@compiletime public int b", "@compiletime function fillB()", " b = 20", "endpackage",
116+
"package C", "@compiletime public int c", "@compiletime function fillC()", " c = 30", "endpackage",
117+
"package D", "@compiletime public int d", "@compiletime function fillD()", " d = 40", "endpackage",
118118
"package Test", "import A", "import B", "import C", "import D", "native testSuccess()", "init",
119119
" if a + b + c + d == 100", " testSuccess()"
120120
};
@@ -139,6 +139,42 @@ public void compiletimeScalarReplaySplittingIsDeterministicAcrossPackages() {
139139
assertEquals("all compiletime scalar values must still be emitted", 4, persistedAssignments);
140140
}
141141

142+
@Test
143+
public void compiletimeScalarMigrationIsDisabledByDefault() {
144+
String compiled = compileLuaWithRunArgs(
145+
"compiletimeScalarMigrationIsDisabledByDefault",
146+
new RunArgs().with("-lua", "-runcompiletimefunctions"),
147+
"package Test",
148+
"int source = 1",
149+
"@compiletime function fill()",
150+
" source = 42",
151+
"native testSuccess()",
152+
"init",
153+
" if source == 1",
154+
" testSuccess()"
155+
);
156+
157+
assertFalse("scalar compiletime state must not be emitted without the opt-in flag", compiled.contains("initCompiletimeScalarState"));
158+
}
159+
160+
@Test
161+
public void compiletimeArrayMigrationIsDisabledByDefault() {
162+
String compiled = compileLuaWithRunArgs(
163+
"compiletimeArrayMigrationIsDisabledByDefault",
164+
new RunArgs().with("-lua", "-runcompiletimefunctions"),
165+
"package Test",
166+
"int array source = [1]",
167+
"@compiletime function fill()",
168+
" source[0] = 42",
169+
"native testSuccess()",
170+
"init",
171+
" if source[0] == 1",
172+
" testSuccess()"
173+
);
174+
175+
assertFalse("array compiletime state must not be emitted without the opt-in annotation", compiled.contains("initCompiletimeArrayState"));
176+
}
177+
142178
@Test
143179
public void compiletimeInterpreterSeesLuaTarget() {
144180
String compiled = compileLuaWithRunArgs(
@@ -149,7 +185,7 @@ public void compiletimeInterpreterSeesLuaTarget() {
149185
"endpackage",
150186
"package Test",
151187
"import MagicFunctions",
152-
"int observedBackend",
188+
"@compiletime int observedBackend",
153189
"@compiletime function detectBackend()",
154190
" if isLua",
155191
" observedBackend = 1",

0 commit comments

Comments
 (0)