Skip to content

Commit 7cbf091

Browse files
committed
Support config overrides for extension functions
1 parent 9eec8a8 commit 7cbf091

5 files changed

Lines changed: 454 additions & 38 deletions

File tree

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package de.peeeq.wurstscript.attributes;
22

3-
import com.google.common.collect.ImmutableCollection;
43
import de.peeeq.wurstscript.ast.*;
5-
import de.peeeq.wurstscript.attributes.names.FuncLink;
64
import de.peeeq.wurstscript.attributes.names.NameLink;
75
import org.eclipse.jdt.annotation.Nullable;
86

@@ -27,16 +25,24 @@ public static NameDef calculate(GlobalVarDef g) {
2725
}
2826

2927
public static NameDef calculate(FuncDef f) {
28+
return configuredFunctionOrSelf(f);
29+
}
30+
31+
public static NameDef calculate(ExtensionFuncDef f) {
32+
return configuredFunctionOrSelf(f);
33+
}
34+
35+
private static NameDef configuredFunctionOrSelf(FunctionDefinition f) {
36+
if (f instanceof FuncDef && f.attrNearestStructureDef() != null) {
37+
return f;
38+
}
3039
WPackage p = getConfigPackage(f);
3140
if (p != null) {
32-
ImmutableCollection<FuncLink> links = p.getElements().lookupFuncsNoConfig(f.getName(), false);
33-
for (NameLink link : links) {
34-
if (hasConfigAnnotation(link.getDef())) {
35-
return link.getDef();
36-
}
41+
FunctionDefinition configured = ConfigFunctionMatcher.findMatchingFunction(p, f, true);
42+
if (configured != null) {
43+
return configured;
3744
}
3845
}
39-
// not configured
4046
return f;
4147
}
4248

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package de.peeeq.wurstscript.attributes;
2+
3+
import de.peeeq.wurstscript.ast.*;
4+
import de.peeeq.wurstscript.attributes.names.DefLink;
5+
import de.peeeq.wurstscript.types.VariableBinding;
6+
import de.peeeq.wurstscript.types.WurstType;
7+
import de.peeeq.wurstscript.types.WurstTypeBoundTypeParam;
8+
import org.eclipse.jdt.annotation.Nullable;
9+
10+
/** Exact ABI matching for package functions replaced through {@code @config}. */
11+
public final class ConfigFunctionMatcher {
12+
13+
private ConfigFunctionMatcher() {
14+
}
15+
16+
public static @Nullable FunctionDefinition findMatchingFunction(WPackage pack, FunctionDefinition function) {
17+
return findMatchingFunction(pack, function, false);
18+
}
19+
20+
public static @Nullable FunctionDefinition findMatchingFunction(WPackage pack, FunctionDefinition function,
21+
boolean requireConfigAnnotation) {
22+
for (DefLink link : pack.getElements().attrNameLinks().get(function.getName())) {
23+
if (link.getDef() instanceof FunctionDefinition candidate
24+
&& isPackageFunction(candidate, pack)
25+
&& (!requireConfigAnnotation || candidate.hasAnnotation("@config"))
26+
&& matches(function, candidate)) {
27+
return candidate;
28+
}
29+
}
30+
return null;
31+
}
32+
33+
private static boolean isPackageFunction(FunctionDefinition function, WPackage pack) {
34+
return function.attrNearestPackage() == pack
35+
&& (!(function instanceof FuncDef) || function.attrNearestStructureDef() == null);
36+
}
37+
38+
public static boolean matches(FunctionDefinition first, FunctionDefinition second) {
39+
if (!first.getName().equals(second.getName())) {
40+
return false;
41+
}
42+
if ((first instanceof ExtensionFuncDef) != (second instanceof ExtensionFuncDef)) {
43+
return false;
44+
}
45+
if (!(first instanceof AstElementWithTypeParameters firstGeneric)
46+
|| !(second instanceof AstElementWithTypeParameters secondGeneric)) {
47+
return false;
48+
}
49+
TypeParamDefs firstTypeParams = firstGeneric.getTypeParameters();
50+
TypeParamDefs secondTypeParams = secondGeneric.getTypeParameters();
51+
if (firstTypeParams.size() != secondTypeParams.size()) {
52+
return false;
53+
}
54+
55+
VariableBinding alphaMapping = VariableBinding.emptyMapping();
56+
for (int i = 0; i < firstTypeParams.size(); i++) {
57+
TypeParamDef firstTypeParam = firstTypeParams.get(i);
58+
TypeParamDef secondTypeParam = secondTypeParams.get(i);
59+
alphaMapping = alphaMapping.set(firstTypeParam,
60+
new WurstTypeBoundTypeParam(firstTypeParam, secondTypeParam.attrTyp(), first));
61+
}
62+
for (int i = 0; i < firstTypeParams.size(); i++) {
63+
TypeParamDef firstTypeParam = firstTypeParams.get(i);
64+
TypeParamDef secondTypeParam = secondTypeParams.get(i);
65+
if (!equalConstraints(firstTypeParam, secondTypeParam, alphaMapping, first)) {
66+
return false;
67+
}
68+
}
69+
70+
if (first instanceof ExtensionFuncDef firstExtension) {
71+
ExtensionFuncDef secondExtension = (ExtensionFuncDef) second;
72+
if (!equalType(firstExtension.getExtendedType().attrTyp(),
73+
secondExtension.getExtendedType().attrTyp(), alphaMapping, first)) {
74+
return false;
75+
}
76+
}
77+
if (first.getParameters().size() != second.getParameters().size()) {
78+
return false;
79+
}
80+
for (int i = 0; i < first.getParameters().size(); i++) {
81+
if (!equalType(first.getParameters().get(i).attrTyp(), second.getParameters().get(i).attrTyp(),
82+
alphaMapping, first)) {
83+
return false;
84+
}
85+
}
86+
return equalType(first.attrReturnTyp(), second.attrReturnTyp(), alphaMapping, first);
87+
}
88+
89+
private static boolean equalConstraints(TypeParamDef first, TypeParamDef second,
90+
VariableBinding alphaMapping, Element location) {
91+
if ((first.getTypeParamConstraints() instanceof TypeExprList)
92+
!= (second.getTypeParamConstraints() instanceof TypeExprList)) {
93+
return false;
94+
}
95+
if (!(first.getTypeParamConstraints() instanceof TypeExprList firstConstraints)) {
96+
return true;
97+
}
98+
TypeExprList secondConstraints = (TypeExprList) second.getTypeParamConstraints();
99+
if (firstConstraints.size() != secondConstraints.size()) {
100+
return false;
101+
}
102+
for (int i = 0; i < firstConstraints.size(); i++) {
103+
if (!equalType(firstConstraints.get(i).attrTyp(), secondConstraints.get(i).attrTyp(),
104+
alphaMapping, location)) {
105+
return false;
106+
}
107+
}
108+
return true;
109+
}
110+
111+
private static boolean equalType(WurstType first, WurstType second,
112+
VariableBinding alphaMapping, Element location) {
113+
return first.setTypeArgs(alphaMapping).equalsType(second, location);
114+
}
115+
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,12 @@ public static ImmutableCollection<FuncLink> lookupMemberFuncs(Element node, Wurs
193193
}
194194
}
195195

196-
ImmutableCollection<FuncLink> immutableResult = removeDuplicates(result);
196+
ImmutableCollection<FuncLink> rawResult = removeDuplicates(result);
197+
ImmutableList.Builder<FuncLink> configuredResult = ImmutableList.builderWithExpectedSize(rawResult.size());
198+
for (FuncLink function : rawResult) {
199+
configuredResult.add(function.withConfigDef());
200+
}
201+
ImmutableCollection<FuncLink> immutableResult = configuredResult.build();
197202

198203
if (!showErrors) {
199204
GlobalCaches.CacheKey key = new GlobalCaches.CacheKey(node, memberFuncCacheName(name, receiverType), GlobalCaches.LookupType.MEMBER_FUNC);

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

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import de.peeeq.wurstscript.attributes.AttrFuncDef;
99
import de.peeeq.wurstscript.attributes.CofigOverridePackages;
1010
import de.peeeq.wurstscript.attributes.CompileError;
11+
import de.peeeq.wurstscript.attributes.ConfigFunctionMatcher;
1112
import de.peeeq.wurstscript.attributes.ImplicitFuncs;
1213
import de.peeeq.wurstscript.attributes.OverloadingResolver;
1314
import de.peeeq.wurstscript.attributes.names.DefLink;
@@ -805,19 +806,9 @@ private void checkConfigOverride(NameDef e) {
805806
+ "It is still possible to configure this var but it is not recommended.");
806807
}
807808

808-
} else if (e instanceof FuncDef) {
809-
FuncDef funcDef = (FuncDef) e;
810-
Collection<FuncLink> funcs = origPackage.getElements().lookupFuncsNoConfig(funcDef.getName(), false);
811-
FuncDef configuredFunc = null;
812-
for (NameLink nameLink : funcs) {
813-
if (nameLink.getDef() instanceof FuncDef) {
814-
FuncDef f = (FuncDef) nameLink.getDef();
815-
if (equalSignatures(funcDef, f)) {
816-
configuredFunc = f;
817-
break;
818-
}
819-
}
820-
}
809+
} else if (e instanceof FuncDef || e instanceof ExtensionFuncDef) {
810+
FunctionDefinition funcDef = (FunctionDefinition) e;
811+
FunctionDefinition configuredFunc = ConfigFunctionMatcher.findMatchingFunction(origPackage, funcDef);
821812
if (configuredFunc == null) {
822813
funcDef.addError("Could not find a function " + funcDef.getName()
823814
+ " with the same signature in the configured package.");
@@ -833,22 +824,6 @@ private void checkConfigOverride(NameDef e) {
833824
}
834825
}
835826

836-
private boolean equalSignatures(FuncDef f, FuncDef g) {
837-
if (f.getParameters().size() != g.getParameters().size()) {
838-
return false;
839-
}
840-
if (!f.attrReturnTyp().equalsType(g.attrReturnTyp(), f)) {
841-
return false;
842-
}
843-
for (int i = 0; i < f.getParameters().size(); i++) {
844-
if (!f.getParameters().get(i).attrTyp().equalsType(g.getParameters().get(i).attrTyp(), f)) {
845-
return false;
846-
}
847-
}
848-
849-
return true;
850-
}
851-
852827
private void checkExprEmpty(ExprEmpty e) {
853828
e.addError("Incomplete expression...");
854829

0 commit comments

Comments
 (0)