Skip to content

Commit ffa65cc

Browse files
committed
keep other things working as before
1 parent fc8fa5c commit ffa65cc

4 files changed

Lines changed: 341 additions & 116 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import de.peeeq.wurstscript.WurstOperator;
77
import de.peeeq.wurstscript.ast.*;
88
import de.peeeq.wurstscript.attributes.names.FuncLink;
9+
import de.peeeq.wurstscript.attributes.names.NameResolution;
910
import de.peeeq.wurstscript.attributes.names.Visibility;
1011
import de.peeeq.wurstscript.types.*;
1112
import de.peeeq.wurstscript.utils.Utils;
@@ -18,6 +19,7 @@
1819
import java.util.function.Predicate;
1920
import java.util.stream.Collectors;
2021

22+
import static de.peeeq.wurstscript.attributes.AttrPossibleFunctionSignatures.*;
2123
import static de.peeeq.wurstscript.attributes.names.NameResolution.lookupMemberFuncs;
2224

2325

@@ -66,55 +68,97 @@ public static FuncLink calculate(final ExprFuncRef node) {
6668
return getExtensionFunction(node.getLeft(), node.getRight(), node.getOp());
6769
}
6870

69-
public static de.peeeq.wurstscript.attributes.names.FuncLink calculate(final ExprMemberMethod node) {
70-
// collect candidates
71+
public static @Nullable FuncLink calculate(final ExprMemberMethod node) {
7172
WurstType recvT = node.getLeft().attrTyp();
72-
String name = node.getFuncName();
73-
var cands = de.peeeq.wurstscript.attributes.names.NameResolution
74-
.lookupMemberFuncs(node, recvT, name, /*showErrors=*/false);
73+
var raw = NameResolution.lookupMemberFuncs(node, recvT, node.getFuncName(), /*showErrors=*/false);
7574

76-
if (cands.isEmpty()) return null;
75+
java.util.ArrayList<FuncLink> visible = new java.util.ArrayList<>(raw.size());
76+
java.util.ArrayList<FuncLink> hidden = new java.util.ArrayList<>(raw.size());
77+
for (var f : raw) {
78+
if (isVisible(f)) visible.add(f); else hidden.add(f);
79+
}
7780

78-
// resolve with arguments using the same rules as signatures:
79-
// build a (sig, link) pair for each candidate and pick the best match.
80-
java.util.List<WurstType> argTypes = de.peeeq.wurstscript.attributes.AttrFuncDef.argumentTypesPre(node);
81-
de.peeeq.wurstscript.attributes.names.FuncLink bestLink = null;
82-
de.peeeq.wurstscript.types.FunctionSignature bestSig = null;
81+
if (!raw.isEmpty() && visible.isEmpty()) {
82+
// Keep the classic diagnostic the tests look for:
83+
node.addError("The method " + node.getFuncName() + " is not visible here.");
84+
return null; // don’t leak a def to downstream passes/codegen
85+
}
8386

84-
// Pass 1: exact matches only
87+
java.util.List<FuncLink> methods = new java.util.ArrayList<>();
88+
java.util.List<FuncLink> exts = new java.util.ArrayList<>();
89+
for (var f : visible) {
90+
if (isExtension(f)) exts.add(f); else methods.add(f);
91+
}
92+
93+
if (!exts.isEmpty()) {
94+
exts = keepMostSpecificReceivers(exts, FuncLink::getReceiverType, node);
95+
}
96+
97+
java.util.ArrayList<FuncLink> cands = new java.util.ArrayList<>(methods.size() + exts.size());
98+
cands.addAll(methods);
99+
cands.addAll(exts);
100+
101+
var argTypes = AttrFuncDef.argumentTypesPre(node);
102+
103+
// Pass 1: exact matches
104+
java.util.ArrayList<FuncLink> exactLinks = new java.util.ArrayList<>();
105+
java.util.ArrayList<FunctionSignature> exactSigs = new java.util.ArrayList<>();
85106
for (var f : cands) {
86-
var sig = de.peeeq.wurstscript.types.FunctionSignature.fromNameLink(f);
87-
var matched = sig.matchAgainstArgs(argTypes, node);
88-
if (matched != null) {
89-
// choose the first exact match; if you want “most specific” tie-break, add it here
90-
bestLink = f;
91-
bestSig = matched;
92-
break;
107+
var sig = FunctionSignature.fromNameLink(f);
108+
var m = sig.matchAgainstArgs(argTypes, node);
109+
if (m != null) {
110+
exactLinks.add(f);
111+
exactSigs.add(m);
93112
}
94113
}
95-
96-
if (bestLink == null) {
97-
// Pass 2: best-effort (to align with diagnostics), pick the lowest badness
98-
int bestBadness = Integer.MAX_VALUE;
99-
for (var f : cands) {
100-
var sig = de.peeeq.wurstscript.types.FunctionSignature.fromNameLink(f);
101-
var res = sig.tryMatchAgainstArgs(argTypes, node.getArgs(), node);
102-
if (res.getBadness() < bestBadness) {
103-
bestBadness = res.getBadness();
104-
bestLink = f;
105-
bestSig = res.getSig();
114+
if (!exactLinks.isEmpty()) {
115+
// methods vs others
116+
java.util.ArrayList<Integer> methodIdxs = new java.util.ArrayList<>();
117+
for (int i = 0; i < exactLinks.size(); i++) {
118+
if (!isExtension(exactLinks.get(i))) methodIdxs.add(i);
119+
}
120+
if (methodIdxs.size() > 1) {
121+
// filter method candidates by most specific receiver
122+
java.util.ArrayList<FunctionSignature> methSigs = new java.util.ArrayList<>();
123+
for (int i : methodIdxs) methSigs.add(exactSigs.get(i));
124+
methSigs = (java.util.ArrayList<FunctionSignature>) keepMostSpecificReceivers(
125+
methSigs, FunctionSignature::getReceiverType, node
126+
);
127+
// pick the first of the survivors
128+
var chosenSig = methSigs.get(0);
129+
// find corresponding link
130+
for (int i = 0; i < exactSigs.size(); i++) {
131+
if (exactSigs.get(i) == chosenSig) {
132+
return exactLinks.get(i).withTypeArgBinding(node, chosenSig.getMapping());
133+
}
106134
}
135+
} else if (methodIdxs.size() == 1) {
136+
int i = methodIdxs.get(0);
137+
return exactLinks.get(i).withTypeArgBinding(node, exactSigs.get(i).getMapping());
138+
} else {
139+
// no methods, only extensions exact → pick first (they were narrowed already)
140+
return exactLinks.get(0).withTypeArgBinding(node, exactSigs.get(0).getMapping());
107141
}
108142
}
109143

110-
if (bestLink == null) return null;
111-
112-
// IMPORTANT: carry the mapping from the matched signature back into the link,
113-
// so downstream types are fully bound at the call site.
114-
var bound = bestLink.withTypeArgBinding(node, bestSig.getMapping());
115-
return bound;
144+
// Pass 2: best-effort (unchanged)
145+
int bestBad = Integer.MAX_VALUE;
146+
FuncLink best = null;
147+
FunctionSignature bestSig = null;
148+
for (var f : cands) {
149+
var sig = FunctionSignature.fromNameLink(f);
150+
var r = sig.tryMatchAgainstArgs(argTypes, node.getArgs(), node);
151+
if (r.getBadness() < bestBad) {
152+
bestBad = r.getBadness();
153+
best = f;
154+
bestSig = r.getSig();
155+
}
156+
}
157+
return best == null ? null : best.withTypeArgBinding(node, bestSig.getMapping());
116158
}
117159

160+
161+
118162
public static @Nullable FunctionDefinition calculateDef(final ExprMemberMethod node) {
119163
var fl = node.attrFuncLink();
120164
return fl == null ? null : fl.getDef();

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFunctionSignature.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,25 @@ public class AttrFunctionSignature {
1818
public static FunctionSignature calculate(StmtCall fc) {
1919
Collection<FunctionSignature> sigs = fc.attrPossibleFunctionSignatures();
2020
FunctionSignature sig = filterSigs(sigs, argTypes(fc), fc);
21+
2122
VariableBinding mapping = sig.getMapping();
2223
for (CompileError error : mapping.getErrors()) {
2324
fc.getErrorHandler().sendError(error);
2425
}
25-
if (mapping.hasUnboundTypeVars()) {
26+
27+
// If any argument is a closure, let it be typed using the selected signature’s
28+
// expected parameter types before complaining about unbound type variables.
29+
boolean hasClosureArg = false;
30+
if (fc instanceof AstElementWithArgs) {
31+
for (Expr a : ((AstElementWithArgs) fc).getArgs()) {
32+
if (a instanceof ExprClosure) {
33+
hasClosureArg = true;
34+
break;
35+
}
36+
}
37+
}
38+
39+
if (mapping.hasUnboundTypeVars() && !hasClosureArg) {
2640
fc.addError("Cannot infer type for type parameter " + mapping.printUnboundTypeVars());
2741
}
2842

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
package de.peeeq.wurstscript.attributes;
22

3-
import de.peeeq.wurstscript.ast.ModuleDef;
4-
import de.peeeq.wurstscript.ast.ModuleInstanciation;
5-
import de.peeeq.wurstscript.ast.TypeDef;
3+
import de.peeeq.wurstscript.ast.*;
64
import de.peeeq.wurstscript.utils.Utils;
75
import org.eclipse.jdt.annotation.Nullable;
86

9-
public class AttrModuleInstanciations {
7+
public final class AttrModuleInstanciations {
8+
9+
private AttrModuleInstanciations() {}
1010

1111
public static @Nullable ModuleDef getModuleOrigin(ModuleInstanciation mi) {
12-
TypeDef def = mi.getParent().lookupType(mi.getName());
13-
if (def instanceof ModuleDef) {
14-
return (ModuleDef) def;
15-
} else {
12+
// NOTE: For ModuleInstanciation the "name" used for resolution has historically been getName().
13+
// Keep this to preserve prior behavior.
14+
final String name = mi.getName();
15+
16+
// 1) Normal path: resolve relative to the lexical parent (old behavior)
17+
final Element parent = mi.getParent();
18+
if (parent != null) {
19+
TypeDef def = parent.lookupType(name, /*showErrors*/ false);
20+
if (def instanceof ModuleDef) {
21+
return (ModuleDef) def;
22+
}
23+
// Attached but not found -> keep the old error
1624
mi.addError("Could not find module origin for " + Utils.printElement(mi));
25+
return null;
26+
}
27+
28+
// 2) Detached during incremental build: try the nearest attached scope
29+
final WScope scope = mi.attrNearestScope();
30+
if (scope != null) {
31+
TypeDef def = scope.lookupType(name, /*showErrors*/ false);
32+
if (def instanceof ModuleDef) {
33+
return (ModuleDef) def;
34+
}
1735
}
36+
37+
// 3) Still not found and we're detached: this can be a transient state,
38+
// so don't emit an error here. Return null and let callers handle gracefully.
1839
return null;
1940
}
20-
2141
}

0 commit comments

Comments
 (0)