22
33import com .google .common .collect .*;
44import de .peeeq .wurstscript .WLogger ;
5+ import de .peeeq .wurstscript .ast .ClassDef ;
56import de .peeeq .wurstscript .ast .PackageOrGlobal ;
67import de .peeeq .wurstscript .ast .WPackage ;
78import de .peeeq .wurstscript .attributes .CompileError ;
89import de .peeeq .wurstscript .jassIm .*;
910import de .peeeq .wurstscript .translation .imtojass .ImAttrType ;
1011import de .peeeq .wurstscript .translation .imtojass .TypeRewriteMatcher ;
12+ import de .peeeq .wurstscript .translation .lua .translation .RemoveGarbage ;
1113import org .eclipse .jdt .annotation .Nullable ;
1214import org .jetbrains .annotations .NotNull ;
1315
@@ -81,6 +83,26 @@ public void transform() {
8183 dbg (summary ("after removeGenericConstructs" ));
8284
8385 dbg (checkDanglingMethodRefs ("end" ));
86+
87+ // TODO fix or remove this check
88+ // assertNoUnspecializedGenericGlobals();
89+ }
90+
91+ private void assertNoUnspecializedGenericGlobals () {
92+ prog .accept (new Element .DefaultVisitor () {
93+ @ Override public void visit (ImVarAccess va ) {
94+ super .visit (va );
95+ if (globalToClass .containsKey (va .getVar ())) {
96+ throw new CompileError (va , "Unspecialized generic global still used: " + va .getVar ().getName ());
97+ }
98+ }
99+ @ Override public void visit (ImVarArrayAccess vaa ) {
100+ super .visit (vaa );
101+ if (globalToClass .containsKey (vaa .getVar ())) {
102+ throw new CompileError (vaa , "Unspecialized generic global array still used: " + vaa .getVar ().getName ());
103+ }
104+ }
105+ });
84106 }
85107
86108 private void makeNullAssignmentsSafe () {
@@ -152,17 +174,50 @@ private String checkDanglingMethodRefs(String phase) {
152174 final int [] dangling = {0 };
153175
154176 prog .accept (new Element .DefaultVisitor () {
155- @ Override public void visit (ImMethodCall mc ) {
177+ @ Override
178+ public void visit (ImMethodCall mc ) {
179+ super .visit (mc );
180+
156181 ImMethod m = mc .getMethod ();
157- if (m != null && !inProg .containsKey (m )) {
158- dangling [0 ]++;
159- dbg ("DANGLING methodCall: method=" + m .getName () + " " + id (m )
182+ if (m == null ) return ;
183+
184+ boolean methodIsGeneric = m .getImplementation () != null && !m .getImplementation ().getTypeVariables ().isEmpty ();
185+
186+ // If method is generic but call carries no type args, try to infer from receiver type and patch them in.
187+ if (methodIsGeneric && mc .getTypeArguments ().isEmpty ()) {
188+ ImClass owning = m .attrClass ();
189+ ImType rt = mc .getReceiver ().attrTyp ();
190+ if (owning != null && rt instanceof ImClassType ) {
191+ ImClassType adapted = adaptToSuperclass ((ImClassType ) rt , owning );
192+ if (adapted != null && !adapted .getTypeArguments ().isEmpty ()) {
193+ List <ImTypeArgument > copied = new ArrayList <>(adapted .getTypeArguments ().size ());
194+ for (ImTypeArgument ta : adapted .getTypeArguments ()) copied .add (ta .copy ());
195+ mc .getTypeArguments ().addAll (0 , copied );
196+
197+ dbg ("Backfilled missing methodCall typeArgs: method=" + m .getName () + " " + id (m )
198+ + " owning=" + owning .getName ()
199+ + " recvType=" + rt
200+ + " inferredTA=" + shortTypeArgs (mc .getTypeArguments ()));
201+ } else {
202+ dbg ("MISSING methodCall typeArgs (cannot infer): method=" + m .getName () + " " + id (m )
203+ + " owning=" + (owning == null ? "null" : owning .getName ())
204+ + " recvType=" + rt );
205+ }
206+ } else {
207+ dbg ("MISSING methodCall typeArgs (no owning/receiverClassType): method=" + m .getName () + " " + id (m )
208+ + " owning=" + (owning == null ? "null" : owning .getName ())
209+ + " recvType=" + shortType (rt ));
210+ }
211+ }
212+
213+ if (!mc .getTypeArguments ().isEmpty ()) {
214+ dbg ("COLLECT GenericMethodCall: method=" + m .getName () + " " + id (m )
160215 + " impl=" + (m .getImplementation () == null ? "null" : (m .getImplementation ().getName () + " " + id (m .getImplementation ())))
161216 + " owningClass=" + (m .attrClass () == null ? "null" : (m .attrClass ().getName () + " " + id (m .attrClass ())))
162- + " receiverType=" + shortType (mc .getReceiver ().attrTyp ())
163- + " callTypeArgs=" + shortTypeArgs (mc .getTypeArguments ()));
217+ + " recvType=" + shortType (mc .getReceiver ().attrTyp ())
218+ + " callTA=" + shortTypeArgs (mc .getTypeArguments ()));
219+ genericsUses .add (new GenericMethodCall (mc ));
164220 }
165- super .visit (mc );
166221 }
167222 });
168223
@@ -226,7 +281,12 @@ private String summary(String phase) {
226281 private void removeNonSpecializedGlobals () {
227282 for (ImVar imVar : specializedGlobals .rowKeySet ()) {
228283 prog .getGlobals ().remove (imVar );
229- prog .getGlobalInits ().remove (imVar );
284+ List <ImSet > inits = prog .getGlobalInits ().remove (imVar );
285+ if (inits != null ) {
286+ for (ImSet init : inits ) {
287+ init .replaceBy (ImHelper .nullExpr ());
288+ }
289+ }
230290 }
231291 }
232292
@@ -366,31 +426,62 @@ private void moveFunctionsOutOfClass(ImClass c) {
366426 * These are the "static" fields that need specialization
367427 */
368428 private void identifyGenericGlobals () {
369- // Build a map of class name to class for quick lookup
370- Map <String , ImClass > classMap = new HashMap <>();
371- for (ImClass c : prog .getClasses ()) {
372- classMap .put (c .getName (), c );
373- }
429+ // Only include "relevant" classes: new-generic or subclass of new-generic.
430+ Map <String , ImClass > relevantClassMap = buildRelevantClassMap ();
374431
375- // Check each global variable to see if it belongs to a generic class
376432 for (ImVar global : prog .getGlobals ()) {
377- // Global variable names for static fields follow the pattern: ClassName_fieldName
378- String varName = global .getName ();
379- int underscoreIdx = varName .indexOf ('_' );
380- if (underscoreIdx > 0 ) {
381- String potentialClassName = varName .substring (0 , underscoreIdx );
382- ImClass owningClass = classMap .get (potentialClassName );
383-
384- if (owningClass != null && !owningClass .getTypeVariables ().isEmpty ()) {
385- // This global belongs to a generic class
386- globalToClass .put (global , owningClass );
387- WLogger .trace ("Identified generic global: " + varName + " of type " + global .getType () +
388- " belonging to class " + owningClass .getName ());
389- }
433+ ImClass owner = resolveOwningClassFromTrace (global , relevantClassMap );
434+ if (owner == null ) {
435+ continue ; // not defined inside a class (package/global constant, etc.)
390436 }
437+
438+ // This global belongs to a relevant (new-generic or inheriting) class:
439+ globalToClass .put (global , owner );
440+ WLogger .trace ("Identified generic static-field global: " + global .getName ()
441+ + " of type " + global .getType ()
442+ + " belonging to class " + owner .getName ());
391443 }
392444 }
393445
446+ /**
447+ * Build a map of class-name -> ImClass, but only for "relevant" classes:
448+ * - the class is new-generic (has typeVariables)
449+ * - OR any of its superclasses is new-generic (transitively)
450+ */
451+ private Map <String , ImClass > buildRelevantClassMap () {
452+ Map <String , ImClass > m = new HashMap <>();
453+ IdentityHashMap <ImClass , Boolean > memo = new IdentityHashMap <>();
454+
455+ for (ImClass c : prog .getClasses ()) {
456+ if (!c .getTypeVariables ().isEmpty ()) {
457+ m .put (c .getName (), c );
458+ }
459+ }
460+ return m ;
461+ }
462+
463+ /**
464+ * Resolve owning class for a global via trace:
465+ * - if the global's trace source is inside a class, return the matching ImClass (if relevant)
466+ * - otherwise return null
467+ */
468+ private @ Nullable ImClass resolveOwningClassFromTrace (ImVar global , Map <String , ImClass > relevantClassMap ) {
469+ if (global .getTrace () == null ) return null ;
470+
471+ // This is the only assumption you may need to adapt if your ImTrace API differs:
472+ de .peeeq .wurstscript .ast .Element srcObj = global .getTrace (); // expected to be a wurst AST Element
473+ if (srcObj == null ) return null ;
474+
475+ @ Nullable ClassDef classDef = srcObj .attrNearestClassDef ();
476+ if (classDef == null ) return null ;
477+
478+ // Get the class name from the AST (no global-name parsing).
479+ String className = classDef .getNameId ().getName ();
480+
481+ // Only accept if it is one of the relevant classes (new-generic or inherits new-generic).
482+ return relevantClassMap .get (className );
483+ }
484+
394485 /**
395486 * When everything is specialized, we can remove generic functions and classes
396487 */
@@ -1169,6 +1260,9 @@ public void eliminate() {
11691260 }
11701261
11711262 private ImVar ensureSpecializedGlobal (ImVar originalGlobal , ImClass owningClass , GenericTypes concreteGenerics ) {
1263+ concreteGenerics = normalizeToClassArity (concreteGenerics , owningClass , "ensureSpecializedGlobal:" + originalGlobal .getName ());
1264+ if (concreteGenerics == null ) return null ;
1265+
11721266 String key = gKey (concreteGenerics );
11731267 ImVar sg = specializedGlobals .get (originalGlobal , key );
11741268 if (sg != null ) return sg ;
@@ -1229,6 +1323,29 @@ class GenericGlobalArrayAccess implements GenericUse {
12291323 }
12301324 }
12311325
1326+ private @ Nullable GenericTypes normalizeToClassArity (GenericTypes g , ImClass owningClass , String why ) {
1327+ int need = owningClass .getTypeVariables ().size ();
1328+ int have = g .getTypeArguments ().size ();
1329+
1330+ if (have == need ) return g ;
1331+
1332+ if (have < need ) {
1333+ dbg ("GEN-ARITY FAIL (" + why + "): class=" + owningClass .getName ()
1334+ + " need=" + need + " have=" + have + " g=" + g );
1335+ return null ;
1336+ }
1337+
1338+ // have > need: take the prefix; this is the common case when the function-context has extra type args.
1339+ List <ImTypeArgument > cut = new ArrayList <>(need );
1340+ for (int i = 0 ; i < need ; i ++) {
1341+ cut .add (g .getTypeArguments ().get (i ).copy ());
1342+ }
1343+ GenericTypes r = new GenericTypes (cut );
1344+ dbg ("GEN-ARITY TRUNC (" + why + "): class=" + owningClass .getName ()
1345+ + " need=" + need + " have=" + have + " g=" + g + " -> " + r );
1346+ return r ;
1347+ }
1348+
12321349 /**
12331350 * NEW: Infer generic types from the enclosing function context
12341351 * For specialized functions, the name contains the type information
@@ -1241,18 +1358,15 @@ private GenericTypes inferGenericsFromFunction(Element element, ImClass owningCl
12411358
12421359 GenericTypes specialized = specializedFunctionGenerics .get (func );
12431360 if (specialized != null ) {
1244- return specialized ;
1361+ return normalizeToClassArity ( specialized , owningClass , "specializedFunctionGenerics:" + func . getName ()) ;
12451362 }
12461363
1247- // If function is still generic, we can't decide yet.
12481364 if (!func .getTypeVariables ().isEmpty ()) {
12491365 return null ;
12501366 }
12511367
12521368 if (!func .getParameters ().isEmpty ()) {
1253- ImVar receiver = func .getParameters ().get (0 );
1254- ImType rt = receiver .getType ();
1255-
1369+ ImType rt = func .getParameters ().get (0 ).getType ();
12561370 if (rt instanceof ImClassType ) {
12571371 ImClassType ct = (ImClassType ) rt ;
12581372 ImClass raw = ct .getClassDef ();
@@ -1263,27 +1377,26 @@ private GenericTypes inferGenericsFromFunction(Element element, ImClass owningCl
12631377 raw .isSubclassOf (owningClass );
12641378
12651379 if (matches ) {
1266- // PRIMARY: use actual type args if present
12671380 if (!ct .getTypeArguments ().isEmpty ()) {
12681381 List <ImTypeArgument > copied = new ArrayList <>(ct .getTypeArguments ().size ());
12691382 for (ImTypeArgument ta : ct .getTypeArguments ()) {
12701383 copied .add (JassIm .ImTypeArgument (ta .getType ().copy (), ta .getTypeClassBinding ()));
12711384 }
1272- return new GenericTypes (copied );
1385+ return normalizeToClassArity ( new GenericTypes (copied ), owningClass , "receiverTypeArgs:" + func . getName () );
12731386 }
12741387
1275- // FALLBACK: parse from specialized class name: Box⟪...⟫
12761388 GenericTypes fromName = extractGenericsFromClassName (raw .getName ());
12771389 if (fromName != null && !fromName .getTypeArguments ().isEmpty ()) {
1278- return fromName ;
1390+ return normalizeToClassArity ( fromName , owningClass , "className:" + raw . getName ()) ;
12791391 }
12801392 }
12811393 }
12821394 }
12831395
12841396 Map <GenericTypes , ImClass > specs = specializedClasses .row (owningClass );
12851397 if (specs .size () == 1 ) {
1286- return specs .keySet ().iterator ().next ();
1398+ GenericTypes only = specs .keySet ().iterator ().next ();
1399+ return normalizeToClassArity (only , owningClass , "singleSpecializationFallback" );
12871400 }
12881401
12891402 return null ;
@@ -1294,6 +1407,7 @@ private GenericTypes inferGenericsFromFunction(Element element, ImClass owningCl
12941407 }
12951408
12961409
1410+
12971411 /**
12981412 * NEW: Extract generic types from a specialized class name like "Box⟪integer⟫"
12991413 */
0 commit comments