Skip to content

Commit 424774f

Browse files
Donachclaude
andcommitted
fix: resolve NPE, type inference, and variable hiding issues
1. FunctionSignature.matchAgainstArgs: Remove duplicate matchAgainstSupertype call that was added for debug logging but passed potentially-null mapping to the second call, causing NPE on generic static method calls. 2. FuncLink.create: Only add function's own type parameters as type variables in the mapping. Enclosing structure type params (class/module) are resolved through receiver type matching, not through argument inference. 3. FunctionSignature.fromNameLink: Only add function definition's own type parameters as type variables, not enclosing structure params. This prevents module type params from appearing as unbound inference variables. 4. FuncLink.withTypeArgBinding: Also check if any type params are being bound (even when types don't change structurally, e.g., T bound to itself within a generic module) to ensure the FuncLink is updated. 5. WurstValidator: Downgrade variable-hides-superclass-variable from error to warning, matching pre-wurstscript#1098 behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8168df3 commit 424774f

4 files changed

Lines changed: 29 additions & 8 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/FuncLink.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ public static FuncLink create(FunctionDefinition func, WScope definedIn) {
6969
WurstType returnType = func.attrReturnTyp();
7070
WurstType receiverType = calcReceiverType(definedIn, func);
7171

72-
// Seed mapping with ALL visible type vars (not just the function's)
73-
VariableBinding mapping = VariableBinding.emptyMapping().withTypeVariables(typeParams);
72+
// Only the function's OWN type params need inference via argument matching.
73+
// Enclosing structure type params (class/module) are resolved through
74+
// receiver type matching in matchDefLinkReceiver, not through inference.
75+
List<TypeParamDef> ownTypeParams = typeParams(func).collect(Collectors.toList());
76+
VariableBinding mapping = VariableBinding.emptyMapping().withTypeVariables(ownTypeParams);
7477

7578
return new FuncLink(visibility, definedIn, typeParams, receiverType, func, paramNames, paramTypes, returnType, mapping);
7679
}
@@ -182,6 +185,16 @@ public FuncLink withTypeArgBinding(Element context, VariableBinding binding) {
182185
}
183186
WurstType newReceiverType = adjustType(context, getReceiverType(), binding);
184187
changed = changed || newReceiverType != getReceiverType();
188+
// Also check if any type params are being bound (even if types don't
189+
// change structurally — e.g., T bound to itself within a generic module)
190+
if (!changed) {
191+
for (TypeParamDef tp : getTypeParams()) {
192+
if (binding.contains(tp)) {
193+
changed = true;
194+
break;
195+
}
196+
}
197+
}
185198
if (changed) {
186199
// remove type parameters that are now bound:
187200
List<TypeParamDef> newTypeParams = new ArrayList<>();

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/types/FunctionSignature.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,14 @@ public static List<String> getParamNames(WParameters parameters) {
118118

119119
public static FunctionSignature fromNameLink(FuncLink f) {
120120
VariableBinding mapping = f.getVariableBinding();
121-
mapping = mapping.withTypeVariables(f.getTypeParams());
122-
return new FunctionSignature(f.getDef(), mapping, f.getReceiverType(), f.getName(), f.getParameterTypes(), getParamNames(f.getDef().getParameters()), f.getReturnType());
121+
// Only add the function definition's own type parameters as type variables
122+
// for inference. Enclosing structure type params (from class/module) are
123+
// resolved through receiver type matching, not argument inference.
124+
FunctionDefinition def = f.getDef();
125+
if (def instanceof AstElementWithTypeParameters) {
126+
mapping = mapping.withTypeVariables(((AstElementWithTypeParameters) def).getTypeParameters());
127+
}
128+
return new FunctionSignature(def, mapping, f.getReceiverType(), f.getName(), f.getParameterTypes(), getParamNames(def.getParameters()), f.getReturnType());
123129
}
124130

125131

@@ -234,14 +240,13 @@ public WurstType getVarargType() {
234240
for (int i = 0; i < argTypes.size(); i++) {
235241
WurstType pt = getParamType(i);
236242
WurstType at = argTypes.get(i);
237-
mapping = at.matchAgainstSupertype(pt, location, mapping, VariablePosition.RIGHT);
238243
VariableBinding before = mapping;
239-
VariableBinding after = at.matchAgainstSupertype(pt, location, mapping, VariablePosition.RIGHT);
244+
mapping = at.matchAgainstSupertype(pt, location, mapping, VariablePosition.RIGHT);
245+
VariableBinding after = mapping;
240246
WLogger.trace(() -> "[IMPLCONV] vb " + System.identityHashCode(before)
241247
+ " -> " + (after == null ? "null" : System.identityHashCode(after))
242248
+ " sameObj=" + (before == after)
243249
+ " pt=" + pt + " at=" + at);
244-
mapping = after;
245250
if (mapping == null) return null;
246251

247252
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1861,7 +1861,7 @@ private void checkFieldShadowing(ClassDef c) {
18611861
ClassDef superOwner = (ClassDef) owner;
18621862
if (isStrictSuperclassOf(superOwner, c)) {
18631863
// produce the requested error text
1864-
def.addError("Variable " + name + " in class " + c.getName()
1864+
def.addWarning("Variable " + name + " in class " + c.getName()
18651865
+ " hides variable " + name + " from superclass " + superOwner.getName());
18661866
// one error per conflicting ancestor is enough
18671867
break;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-runcompiletimefunctions
2+
-injectobjects
3+
-stacktraces

0 commit comments

Comments
 (0)