Skip to content

Commit e90e4c8

Browse files
committed
Implement func sign noneTypeAllowed per param + Fix dor/dand signatures
- Implement function signature none type allowed boolean per Param instead of per FunctionSignature. This is reflected in the function signatures string. - Apply none type allowed boolean to all type of Param instead of only to varparams. - Update function signatures of `dand()` and `dor()`. Fixes NPE when calling `dor()` without arguments (given that SA is enabled).
1 parent 4b5c72a commit e90e4c8

4 files changed

Lines changed: 132 additions & 23 deletions

File tree

src/main/java/com/laytonsmith/core/compiler/signature/FunctionSignature.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,17 @@ public class FunctionSignature {
2020
private final ReturnType returnType;
2121
private final List<Param> params;
2222
private final List<Throws> throwsList;
23-
private boolean noneIsAllowed;
2423

2524
/**
2625
* Creates a new {@link FunctionSignature} with the given properties.
2726
* @param returnType - The function return type.
2827
* @param params - The function parameters.
2928
* @param throwsList - The list of possibly thrown exceptions by the function.
30-
* @param noneIsAllowed - If the none (Java {@code null}) type is allowed and should be treated as
31-
* {@link CClassType.AUTO}.
3229
*/
33-
public FunctionSignature(ReturnType returnType, List<Param> params, List<Throws> throwsList, boolean noneIsAllowed) {
30+
public FunctionSignature(ReturnType returnType, List<Param> params, List<Throws> throwsList) {
3431
this.returnType = returnType;
3532
this.params = params;
3633
this.throwsList = throwsList;
37-
this.noneIsAllowed = noneIsAllowed;
3834
}
3935

4036
/**
@@ -43,7 +39,7 @@ public FunctionSignature(ReturnType returnType, List<Param> params, List<Throws>
4339
* @param returnType - The function return type.
4440
*/
4541
public FunctionSignature(ReturnType returnType) {
46-
this(returnType, new ArrayList<>(), new ArrayList<>(), false);
42+
this(returnType, new ArrayList<>(), new ArrayList<>());
4743
}
4844

4945
/**
@@ -61,10 +57,6 @@ protected void addThrows(Throws throwsObj) {
6157
this.throwsList.add(throwsObj);
6258
}
6359

64-
protected void setNoneIsAllowed(boolean noneIsAllowed) {
65-
this.noneIsAllowed = noneIsAllowed;
66-
}
67-
6860
/**
6961
* Gets the function's return type.
7062
* @return The return type.
@@ -109,7 +101,8 @@ public boolean matches(List<CClassType> argTypes, Environment env, boolean allow
109101

110102
// Match normal or optional parameter.
111103
if(argIndex < argTypes.size()
112-
&& InstanceofUtil.isInstanceof(argTypes.get(argIndex), param.getType(), env)) {
104+
&& ((param.getNoneTypeAllowed() && argTypes.get(argIndex) == null)
105+
|| InstanceofUtil.isInstanceof(argTypes.get(argIndex), param.getType(), env))) {
113106

114107
// Keep track of the optional parameter match.
115108
if(param.isOptional()) {
@@ -130,7 +123,7 @@ public boolean matches(List<CClassType> argTypes, Environment env, boolean allow
130123
// Match as many arguments as possible with this varparam.
131124
int numMatches = 0;
132125
while(argIndex < argTypes.size()
133-
&& ((argTypes.get(argIndex) == null && this.noneIsAllowed)
126+
&& ((param.getNoneTypeAllowed() && argTypes.get(argIndex) == null)
134127
|| InstanceofUtil.isInstanceof(argTypes.get(argIndex), param.getType(), env))) {
135128
argIndex++;
136129
numMatches++;
@@ -172,7 +165,8 @@ public boolean matches(List<CClassType> argTypes, Environment env, boolean allow
172165
*/
173166
public String getParamTypesString() {
174167
return "(" + StringUtils.Join(this.params, ", ", null, null, null, (Param param) -> {
175-
String ret = (param.getType() == null ? "any" : param.getType().getSimpleName());
168+
String ret = (param.getType() == null ? "any" : (param.getNoneTypeAllowed()
169+
? param.getType().getSimpleName() + "|none" : param.getType().getSimpleName()));
176170
if(param.isVarParam()) {
177171
ret += "...";
178172
}

src/main/java/com/laytonsmith/core/compiler/signature/Param.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class Param {
1313
private final String desc;
1414
private final boolean isVarParam;
1515
private final boolean isOptional;
16+
private final boolean noneTypeAllowed;
1617

1718
/**
1819
* Creates a new {@link Param} with the given properties.
@@ -24,14 +25,34 @@ public class Param {
2425
* of the given type, or one argument of type {@code array<type>}. {@code false} otherwise.
2526
* Note that a varparam is only usable as type {@code array<type>}.
2627
* @param isOptional - {@code true} if the parameter is optional, {@code false} otherwise.
28+
* @param noneTypeAllowed - {@code true} if the none (Java {@code null}) type is allowed to match this parameter in
29+
* addition to what the parameter type already matches.
30+
* {@code false} otherwise. If the parameter type is {@code null}, then this boolean parameter is ignored.
2731
*/
28-
public Param(CClassType type, String name, String desc, boolean isVarParam, boolean isOptional) {
32+
public Param(CClassType type, String name, String desc,
33+
boolean isVarParam, boolean isOptional, boolean noneTypeAllowed) {
2934
assert !isVarParam || !isOptional : "A parameter cannot be variadic and optional at the same time.";
3035
this.type = type;
3136
this.name = name;
3237
this.desc = desc;
3338
this.isVarParam = isVarParam;
3439
this.isOptional = isOptional;
40+
this.noneTypeAllowed = noneTypeAllowed;
41+
}
42+
43+
/**
44+
* Creates a new {@link Param} with the given properties.
45+
* Parameters cannot be variadic and optional at the same time, as varparams already imply optionality.
46+
* @param type - The (parent) type of the parameter.
47+
* @param name - The name of the parameter.
48+
* @param desc - The description of the parameter.
49+
* @param isVarParam - {@code true} if the parameter is a varparam, meaning that it matches zero or more arguments
50+
* of the given type, or one argument of type {@code array<type>}. {@code false} otherwise.
51+
* Note that a varparam is only usable as type {@code array<type>}.
52+
* @param isOptional - {@code true} if the parameter is optional, {@code false} otherwise.
53+
*/
54+
public Param(CClassType type, String name, String desc, boolean isVarParam, boolean isOptional) {
55+
this(type, name, desc, isVarParam, isOptional, false);
3556
}
3657

3758
/**
@@ -85,4 +106,15 @@ public boolean isVarParam() {
85106
public boolean isOptional() {
86107
return this.isOptional;
87108
}
109+
110+
/**
111+
* Gets whether the none type is allowed to match this parameter in addition to what the parameter type matches.
112+
* When this returns {@code false}, the none type can still match this parameter if {@ref #getType()} returns
113+
* {@code null}.
114+
* @return {@code true} if a none type argument can match this parameter in addition to what the parameter type
115+
* matches, {@code false} otherwise.
116+
*/
117+
public boolean getNoneTypeAllowed() {
118+
return this.noneTypeAllowed;
119+
}
88120
}

src/main/java/com/laytonsmith/core/compiler/signature/SignatureBuilder.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ public SignatureBuilder(CClassType returnType, String returnValDesc, MatchType m
5656
this.signature = new FunctionSignature(new ReturnType(returnType, returnValDesc));
5757
}
5858

59+
/**
60+
* Adds a normal function parameter. Parameters should be added from left to right.
61+
* @param paramType - The {@link CClassType} of the parameter.
62+
* @param paramName - The name of the parameter.
63+
* @param paramDesc - The description of the parameter.
64+
* @param isOptional - Whether the parameter is optional or not.
65+
* @param noneTypeAllowed - Whether a none type (Java {@code null}) is allowed to match this parameter in addition
66+
* to what the parameter type already matches.
67+
* @return This {@link SignatureBuilder}, for chaining builder methods.
68+
*/
69+
public SignatureBuilder param(
70+
CClassType paramType, String paramName, String paramDesc, boolean isOptional, boolean noneTypeAllowed) {
71+
this.signature.addParam(new Param(paramType, paramName, paramDesc, false, isOptional, noneTypeAllowed));
72+
return this;
73+
}
74+
5975
/**
6076
* Adds a normal function parameter. Parameters should be added from left to right.
6177
* @param paramType - The {@link CClassType} of the parameter.
@@ -80,6 +96,21 @@ public SignatureBuilder param(CClassType paramType, String paramName, String par
8096
return this.param(paramType, paramName, paramDesc, false);
8197
}
8298

99+
/**
100+
* Adds a variadic function parameter (varparam). Parameters should be added from left to right.
101+
* @param paramType - The {@link CClassType} of the parameter (the 'paramType' in 'paramType... paramName').
102+
* @param paramName - The name of the parameter.
103+
* @param paramDesc - The description of the parameter.
104+
* @param noneTypeAllowed - Whether a none type (Java {@code null}) is allowed to match an entry in this parameter
105+
* in addition to what the parameter type already matches.
106+
* @return This {@link SignatureBuilder}, for chaining builder methods.
107+
*/
108+
public SignatureBuilder varParam(
109+
CClassType paramType, String paramName, String paramDesc, boolean noneTypeAllowed) {
110+
this.signature.addParam(new Param(paramType, paramName, paramDesc, true, false, noneTypeAllowed));
111+
return this;
112+
}
113+
83114
/**
84115
* Adds a variadic function parameter (varparam). Parameters should be added from left to right.
85116
* @param paramType - The {@link CClassType} of the parameter (the 'paramType' in 'paramType... paramName').
@@ -103,11 +134,6 @@ public SignatureBuilder throwsEx(Class<? extends CREThrowable> exception, String
103134
return this;
104135
}
105136

106-
public SignatureBuilder setNoneIsAllowed(boolean allowed) {
107-
this.signature.setNoneIsAllowed(allowed);
108-
return this;
109-
}
110-
111137
/**
112138
* Finalizes the last function signature and starts a new function signature with the given return type.
113139
* @param returnType - The return type of the new function signature.

src/main/java/com/laytonsmith/core/functions/BasicLogic.java

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
1717
import com.laytonsmith.core.compiler.signature.FunctionSignatures;
1818
import com.laytonsmith.core.compiler.signature.SignatureBuilder;
19-
import com.laytonsmith.core.constructs.Auto;
2019
import com.laytonsmith.core.constructs.CArray;
2120
import com.laytonsmith.core.constructs.CBoolean;
2221
import com.laytonsmith.core.constructs.CClassType;
@@ -28,6 +27,7 @@
2827
import com.laytonsmith.core.constructs.CString;
2928
import com.laytonsmith.core.constructs.CSymbol;
3029
import com.laytonsmith.core.constructs.CVoid;
30+
import com.laytonsmith.core.constructs.InstanceofUtil;
3131
import com.laytonsmith.core.constructs.Target;
3232
import com.laytonsmith.core.environments.Environment;
3333
import com.laytonsmith.core.environments.GlobalEnv;
@@ -1275,7 +1275,37 @@ public FunctionSignatures getSignatures() {
12751275
* Note that getReturnType could be overridden, not using this signature for typechecking.
12761276
* That implementation is not yet possible until A OR B OR ... types can be described.
12771277
*/
1278-
return new SignatureBuilder(CClassType.AUTO).varParam(Mixed.TYPE, "vals", null).build();
1278+
return new SignatureBuilder(CClassType.AUTO)
1279+
.varParam(Mixed.TYPE, "vals", "The values.")
1280+
.newSignature(CClassType.AUTO)
1281+
.param(Mixed.TYPE, "val", "The first value.")
1282+
.varParam(Mixed.TYPE, "vals", "The values.")
1283+
.param(Mixed.TYPE, "termVal", "The final terminating (non-returning) value.", true, true).build();
1284+
}
1285+
1286+
@Override
1287+
public CClassType getReturnType(Target t, List<CClassType> argTypes,
1288+
List<Target> argTargets, Environment env, Set<ConfigCompileException> exceptions) {
1289+
1290+
// Get return type based on the function signatures. This generates all necessary compile errors.
1291+
CClassType retType = super.getReturnType(t, argTypes, argTargets, env, exceptions);
1292+
1293+
// Return an occurring argument type if all argument types extend that type.
1294+
// TODO - Make this return a multitype instead as soon as all typechecking code supports multitypes.
1295+
search:
1296+
for(CClassType type1 : argTypes) {
1297+
if(type1 != null) {
1298+
for(CClassType type2 : argTypes) {
1299+
if(type2 != null && !InstanceofUtil.isInstanceof(type2, type1, env)) {
1300+
continue search;
1301+
}
1302+
}
1303+
return type1;
1304+
}
1305+
}
1306+
1307+
// Return super result.
1308+
return retType;
12791309
}
12801310

12811311
@Override
@@ -1570,8 +1600,35 @@ public FunctionSignatures getSignatures() {
15701600
* Note that getReturnType could be overridden, not using this signature for typechecking.
15711601
* That implementation is not yet possible until A OR B OR ... types can be described.
15721602
*/
1573-
return new SignatureBuilder(Auto.TYPE).varParam(Mixed.TYPE, "vals", null)
1574-
.setNoneIsAllowed(true).build();
1603+
return new SignatureBuilder(CClassType.AUTO)
1604+
.param(Mixed.TYPE, "val", "The first value.")
1605+
.varParam(Mixed.TYPE, "vals", "The values.")
1606+
.param(Mixed.TYPE, "termVal", "The final terminating (non-returning) value.", true, true).build();
1607+
}
1608+
1609+
@Override
1610+
public CClassType getReturnType(Target t, List<CClassType> argTypes,
1611+
List<Target> argTargets, Environment env, Set<ConfigCompileException> exceptions) {
1612+
1613+
// Get return type based on the function signatures. This generates all necessary compile errors.
1614+
CClassType retType = super.getReturnType(t, argTypes, argTargets, env, exceptions);
1615+
1616+
// Return an occurring argument type if all argument types extend that type.
1617+
// TODO - Make this return a multitype instead as soon as all typechecking code supports multitypes.
1618+
search:
1619+
for(CClassType type1 : argTypes) {
1620+
if(type1 != null) {
1621+
for(CClassType type2 : argTypes) {
1622+
if(type2 != null && !InstanceofUtil.isInstanceof(type2, type1, env)) {
1623+
continue search;
1624+
}
1625+
}
1626+
return type1;
1627+
}
1628+
}
1629+
1630+
// Return super result.
1631+
return retType;
15751632
}
15761633

15771634
@Override

0 commit comments

Comments
 (0)