@@ -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 ();
0 commit comments