Skip to content

Commit 9fd68e1

Browse files
committed
fix stackframes
1 parent c7c44d8 commit 9fd68e1

6 files changed

Lines changed: 151 additions & 93 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -34,43 +34,12 @@ public static ILconst eval(ImFuncRef e, ProgramState globalState, LocalState loc
3434
ImFunction f = e.getFunc();
3535
ImExprs arguments = e.getArguments();
3636

37-
38-
// Evaluate arguments
3937
ILconst[] args = new ILconst[arguments.size()];
4038
for (int i = 0; i < arguments.size(); i++) {
4139
args[i] = arguments.get(i).evaluate(globalState, localState);
4240
}
4341

44-
Map<ImTypeVar, ImType> typeSubstitutions = new HashMap<>();
45-
@Nullable ILconstObject receiver = null;
46-
if (e.getFunc().getParent() != null) {
47-
Element parent = e.getFunc().getParent().getParent();
48-
if (parent instanceof ImClass) {
49-
ImTypeVars typeParams = ((ImClass) parent).getTypeVariables(); // The T74 parameters
50-
ImTypeArguments typeArgs = e.getTypeArguments(); // The <integer> arguments
51-
52-
// Create mapping: T74 -> integer
53-
for (int i = 0; i < typeParams.size() && i < typeArgs.size(); i++) {
54-
ImTypeVar genericParam = typeParams.get(i);
55-
ImType concreteArg = typeArgs.get(i).getType();
56-
typeSubstitutions.put(genericParam, concreteArg);
57-
}
58-
59-
if (args.length > 0 && args[0] instanceof ILconstObject) {
60-
receiver = (ILconstObject) args[0];
61-
}
62-
63-
}
64-
}
65-
66-
globalState.pushStackframeWithTypes(f, receiver, args, e.attrTrace().attrErrorPos(), typeSubstitutions);
67-
68-
69-
try {
70-
return ILInterpreter.runFunc(globalState, f, e, args).getReturnVal();
71-
} finally {
72-
globalState.popStackframe();
73-
}
42+
return ILInterpreter.runFunc(globalState, f, e, args).getReturnVal();
7443
}
7544

7645
public static @Nullable ILconst evaluateFunc(ProgramState globalState,
@@ -246,12 +215,7 @@ public static ILconst eval(ImVarArrayAccess e, ProgramState globalState, LocalSt
246215
typeSubstitutions.put(typeParams.get(i), typeArgs.get(i).getType());
247216
}
248217

249-
globalState.pushStackframeWithTypes(impl, receiver, eargs, mc.attrTrace().attrErrorPos(), typeSubstitutions);
250-
try {
251-
return evaluateFunc(globalState, impl, mc, eargs);
252-
} finally {
253-
globalState.popStackframe();
254-
}
218+
return evaluateFunc(globalState, impl, mc, eargs);
255219
}
256220

257221

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,14 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab
4949
if (Thread.currentThread().isInterrupted()) {
5050
throw new InterpreterException(globalState, "Execution interrupted");
5151
}
52+
5253
try {
54+
// --- varargs rewrite ---
5355
if (f.hasFlag(FunctionFlagEnum.IS_VARARG)) {
54-
// for vararg functions, rewrite args and put last argument
5556
ILconst[] newArgs = new ILconst[f.getParameters().size()];
56-
if (newArgs.length - 1 >= 0) System.arraycopy(args, 0, newArgs, 0, newArgs.length - 1);
57+
if (newArgs.length - 1 >= 0) {
58+
System.arraycopy(args, 0, newArgs, 0, newArgs.length - 1);
59+
}
5760

5861
ILconst[] varargArray = new ILconst[1 + args.length - newArgs.length];
5962
for (int i = newArgs.length - 1, j = 0; i < args.length; i++, j++) {
@@ -63,87 +66,117 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab
6366
args = newArgs;
6467
}
6568

69+
// --- arg count check ---
6670
if (f.getParameters().size() != args.length) {
6771
throw new Error("wrong number of parameters when calling func " + f.getName() + "(" +
6872
Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", ")) + ")");
6973
}
7074

75+
// --- adjust argument constants to expected primitive types (int->real etc.) ---
7176
for (int i = 0; i < f.getParameters().size(); i++) {
72-
// TODO could do typecheck here
7377
args[i] = adjustTypeOfConstant(args[i], f.getParameters().get(i).getType());
7478
}
7579

80+
// --- natives / compiletimenative ---
7681
if (isCompiletimeNative(f) || f.isNative()) {
7782
return runBuiltinFunction(globalState, f, args);
7883
}
7984

85+
// --- local state & bind parameters ---
8086
LocalState localState = new LocalState();
81-
82-
// Set up local variables
83-
int i = 0;
84-
for (ImVar p : f.getParameters()) {
85-
localState.setVal(p, args[i]);
86-
i++;
87+
for (int i = 0; i < f.getParameters().size(); i++) {
88+
localState.setVal(f.getParameters().get(i), args[i]);
8789
}
8890

91+
// --- stacktrace bookkeeping ---
8992
if (f.getBody().isEmpty()) {
90-
return localState.setReturnVal(ILconstNull.instance());
93+
// Still create a stackframe for correct type resolution / tracing symmetry
94+
Map<ImTypeVar, ImType> normalized = Collections.emptyMap();
95+
@Nullable ILconstObject receiverObj = null;
96+
if (args.length > 0 && args[0] instanceof ILconstObject) {
97+
receiverObj = (ILconstObject) args[0];
98+
}
99+
WPos pos = (caller != null) ? caller.attrTrace().attrErrorPos() : f.attrTrace().attrErrorPos();
100+
globalState.pushStackframeWithTypes(f, receiverObj, args, pos, normalized);
101+
try {
102+
return localState.setReturnVal(ILconstNull.instance());
103+
} finally {
104+
globalState.popStackframe();
105+
}
91106
} else {
92107
globalState.setLastStatement(f.getBody().get(0));
93108
}
94109

110+
// --- build type substitutions (for pre-generic-elimination runs) ---
111+
@Nullable ILconstObject receiverObj = null;
112+
if (args.length > 0 && args[0] instanceof ILconstObject) {
113+
receiverObj = (ILconstObject) args[0];
114+
}
95115

96-
if (!(caller instanceof ImFunctionCall)) {
97-
if (caller instanceof ImMethodCall) {
98-
// Instance method call: bind class T-vars from the *receiver*'s concrete type args
99-
final Map<ImTypeVar, ImType> subst = new HashMap<>();
116+
Map<ImTypeVar, ImType> subst = new HashMap<>();
100117

101-
// First parameter is the implicit 'this'
102-
final ImVar thisParam = f.getParameters().get(0);
103-
final ImType thisParamType = thisParam.getType();
104-
if (!(thisParamType instanceof ImClassType)) {
105-
// Defensive: still push with no substitutions
106-
globalState.pushStackframeWithTypes(f, null, args, f.attrTrace().attrErrorPos(), Collections.emptyMap());
107-
} else {
108-
final ImClassType sigThisType = (ImClassType) thisParamType; // may contain ImTypeVarRefs
109-
final ILconstObject thisArg = (ILconstObject) args[0];
110-
final ImClassType recvType = thisArg.getType(); // concrete type Box<tuple<int,int>> etc.
118+
// A) Bind class type vars from receiver (for instance methods / funcs with this as first param)
119+
if (receiverObj != null && !f.getParameters().isEmpty()) {
120+
ImType p0t = f.getParameters().get(0).getType();
121+
if (p0t instanceof ImClassType) {
122+
ImClassType sigThisType = (ImClassType) p0t; // may contain ImTypeVarRefs
123+
ImClass cls = sigThisType.getClassDef();
124+
ImTypeVars tvars = cls.getTypeVariables(); // e.g. [T951]
125+
ImTypeArguments concreteArgs = receiverObj.getType().getTypeArguments(); // e.g. [integer]
111126

112-
// Class type variables (on the class definition)
113-
final ImClass cls = sigThisType.getClassDef();
114-
final ImTypeVars tvars = cls.getTypeVariables(); // e.g., [T74]
127+
int n = Math.min(tvars.size(), concreteArgs.size());
128+
for (int i2 = 0; i2 < n; i2++) {
129+
subst.put(tvars.get(i2), concreteArgs.get(i2).getType());
130+
}
131+
}
132+
}
115133

116-
// Concrete type arguments from receiver (same order)
117-
final ImTypeArguments concreteArgs = recvType.getTypeArguments();
134+
// B) Bind function type vars from explicit call type arguments (generic free functions / constructors etc.)
135+
if (caller instanceof ImFunctionCall) {
136+
ImFunctionCall fc = (ImFunctionCall) caller;
137+
ImTypeVars fvars = f.getTypeVariables();
138+
ImTypeArguments targs = fc.getTypeArguments();
118139

119-
final int n = Math.min(tvars.size(), concreteArgs.size());
120-
for (int i2 = 0; i2 < n; i2++) {
121-
subst.put(tvars.get(i2), concreteArgs.get(i2).getType());
122-
}
140+
int n = Math.min(fvars.size(), targs.size());
141+
for (int i2 = 0; i2 < n; i2++) {
142+
subst.put(fvars.get(i2), targs.get(i2).getType());
143+
}
144+
}
123145

124-
globalState.pushStackframeWithTypes(f, thisArg, args, f.attrTrace().attrErrorPos(), subst);
125-
}
126-
} else {
127-
// Static function or unknown caller kind
128-
globalState.pushStackframeWithTypes(f, null, args, f.attrTrace().attrErrorPos(), Collections.emptyMap());
146+
// C) Normalize RHS through existing frames (so nested substitutions resolve)
147+
Map<ImTypeVar, ImType> normalized = new HashMap<>();
148+
for (Map.Entry<ImTypeVar, ImType> e : subst.entrySet()) {
149+
ImType rhs = globalState.resolveType(e.getValue());
150+
if (rhs instanceof ImTypeVarRef && ((ImTypeVarRef) rhs).getTypeVariable() == e.getKey()) {
151+
continue; // skip self-maps
129152
}
153+
normalized.put(e.getKey(), rhs);
130154
}
131155

156+
// --- single push/pop responsibility ---
157+
WPos pos = (caller != null) ? caller.attrTrace().attrErrorPos() : f.attrTrace().attrErrorPos();
158+
globalState.pushStackframeWithTypes(f, receiverObj, args, pos, normalized);
132159

160+
ILconst retVal = null;
161+
boolean didReturn = false;
133162

134163
try {
135164
f.getBody().runStatements(globalState, localState);
136-
globalState.popStackframe();
137165
} catch (ReturnException e) {
166+
retVal = adjustTypeOfConstant(e.getVal(), f.getReturnType());
167+
didReturn = true;
168+
} finally {
138169
globalState.popStackframe();
139-
ILconst retVal = e.getVal();
140-
retVal = adjustTypeOfConstant(retVal, f.getReturnType());
170+
}
171+
172+
if (didReturn) {
141173
return localState.setReturnVal(retVal);
142174
}
143175

144176
if (f.getReturnType() instanceof ImVoid) {
145177
return localState;
146178
}
179+
147180
throw new InterpreterException("function " + f.getName() + " did not return any value...");
148181

149182
} catch (InterpreterException e) {
@@ -160,6 +193,7 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab
160193
}
161194
}
162195

196+
163197
public static de.peeeq.wurstscript.ast.Element getTrace(ProgramState globalState, ImFunction f) {
164198
Element lastStatement = globalState.getLastStatement();
165199
return lastStatement == null ? f.attrTrace() : lastStatement.attrTrace();

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ public void pushStackframeWithTypes(ImFunction f, @Nullable ILconstObject receiv
180180
normalized.put(e.getKey(), rhs);
181181
}
182182

183-
// System.out.println("pushStackframe " + f + " with receiver " + receiver
184-
// + " and args " + Arrays.toString(args) + " and typesubst " + normalized);
183+
System.out.println("pushStackframe " + f + " with receiver " + receiver
184+
+ " and args " + Arrays.toString(args) + " and typesubst " + normalized);
185+
186+
// new Exception().printStackTrace(System.out);
185187

186188
stackFrames.push(new ILStackFrame(f, receiver, args, trace, normalized));
187189
de.peeeq.wurstscript.jassIm.Element stmt = this.lastStatement;
@@ -359,7 +361,7 @@ public ImType case_ImArrayTypeMulti(ImArrayTypeMulti t) {
359361
}
360362

361363
public void pushStackframe(ImCompiletimeExpr f, WPos trace) {
362-
// System.out.println("pushStackframe compiletime expr " + f);
364+
System.out.println("pushStackframe compiletime expr " + f);
363365
stackFrames.push(new ILStackFrame(f, trace));
364366
de.peeeq.wurstscript.jassIm.Element stmt = this.lastStatement;
365367
if (stmt == null) {
@@ -369,7 +371,8 @@ public void pushStackframe(ImCompiletimeExpr f, WPos trace) {
369371
}
370372

371373
public void popStackframe() {
372-
// System.out.println("popStackframe " + (stackFrames.isEmpty() ? "empty" : stackFrames.peek().f));
374+
// new Exception().printStackTrace(System.out);
375+
System.out.println("popStackframe " + (stackFrames.isEmpty() ? "empty" : stackFrames.peek().f));
373376
if (!stackFrames.isEmpty()) {
374377
stackFrames.pop();
375378
}
@@ -491,6 +494,9 @@ public String instantiationKey(ImClassType ct) {
491494

492495
@Override
493496
public void setVal(ImVar v, ILconst val) {
497+
if (v.isGlobal() && v.getName().contains("A_LLM_t")) {
498+
System.out.println("set " + v.getName() + " @" + System.identityHashCode(v));
499+
}
494500
String key = genericStaticKey(v);
495501
if (key != null) {
496502
genericStaticVals.put(key, val);
@@ -501,6 +507,9 @@ public void setVal(ImVar v, ILconst val) {
501507

502508
@Override
503509
public @Nullable ILconst getVal(ImVar v) {
510+
if (v.isGlobal() && v.getName().contains("A_LLM_t")) {
511+
System.out.println("get " + v.getName() + " @" + System.identityHashCode(v));
512+
}
504513
String key = genericStaticKey(v);
505514
if (key != null) {
506515
ILconst existing = genericStaticVals.get(key);

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,25 @@ public void transform() {
4747

4848
addMemberTypeArguments();
4949

50-
// NEW: Identify generic globals before collecting usages
5150
identifyGenericGlobals();
5251

5352
collectGenericUsages();
5453

5554
eliminateGenericUses();
5655

56+
removeNonSpecializedGlobals();
57+
5758
removeGenericConstructs();
5859

5960
}
6061

62+
private void removeNonSpecializedGlobals() {
63+
for (ImVar imVar : specializedGlobals.rowKeySet()) {
64+
prog.getGlobals().remove(imVar);
65+
prog.getGlobalInits().remove(imVar);
66+
}
67+
}
68+
6169
private void onSpecializeClass(ImClass orig, BiConsumer<GenericTypes, ImClass> action) {
6270
onSpecializedClassTriggers.put(orig, action);
6371
specializedClasses.row(orig).forEach(action);
@@ -451,6 +459,7 @@ private ImClass specializeClass(ImClass c, GenericTypes generics) {
451459
// NEW: Create specialized global variables for this class instantiation
452460
createSpecializedGlobals(c, generics, typeVars);
453461

462+
454463
onSpecializedClassTriggers.get(c).forEach(consumer ->
455464
consumer.accept(generics, newC));
456465
return newC;
@@ -486,8 +495,12 @@ private void createSpecializedGlobals(ImClass originalClass, GenericTypes generi
486495
originalGlobal.getIsBJ()
487496
);
488497

489-
// Add to program globals
490-
prog.getGlobals().add(specializedGlobal);
498+
List<ImSet> originalInits = prog.getGlobalInits().get(originalGlobal);
499+
if (originalInits != null && !originalInits.isEmpty()) {
500+
translator.addGlobalWithInitalizer(specializedGlobal, originalInits.getFirst().getRight().copy());
501+
} else {
502+
translator.addGlobal(specializedGlobal);
503+
}
491504

492505
// Track the specialization
493506
specializedGlobals.put(originalGlobal, generics, specializedGlobal);
@@ -496,8 +509,8 @@ private void createSpecializedGlobals(ImClass originalClass, GenericTypes generi
496509
" with type " + specializedType +
497510
" for generics " + generics);
498511
}
499-
}
500512

513+
}
501514

502515
/**
503516
* Collects all usages from non-generic functions

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1928,7 +1928,7 @@ public void genericModuleThistypeSmall() {
19281928
"package test",
19291929
" native testSuccess()",
19301930
" module LLM",
1931-
" static thistype t",
1931+
" static thistype t = null",
19321932
" construct()",
19331933
" t = this",
19341934
" function iterator() returns Iterator",
@@ -1967,5 +1967,27 @@ public void genericClassWithStaticMember() {
19671967
);
19681968
}
19691969

1970+
@Test
1971+
public void genericClassWithStaticMemberArray() {
1972+
testAssertOkLines(true,
1973+
"package test",
1974+
" native testSuccess()",
1975+
" class A<T:>",
1976+
" static int array foo",
1977+
" function setFoo(int v)",
1978+
" foo[1] = v",
1979+
" function getFoo() returns int",
1980+
" return foo[1]",
1981+
" init",
1982+
" let a = new A<int>",
1983+
" let b = new A<string>",
1984+
" a.setFoo(3)",
1985+
" b.setFoo(1)",
1986+
" if a.getFoo() == 3 and b.getFoo() == 1",
1987+
" testSuccess()",
1988+
"endpackage"
1989+
);
1990+
}
1991+
19701992

19711993
}

0 commit comments

Comments
 (0)