Skip to content

Commit 4575c83

Browse files
committed
Typecheck return()
Typecheck `return;` and `return @Val;` value against its expected type from the surrounding procedure, closure or bind.
1 parent 0bed6bd commit 4575c83

File tree

7 files changed

+121
-5
lines changed

7 files changed

+121
-5
lines changed

src/main/java/com/laytonsmith/core/compiler/analysis/IncludeReference.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class IncludeReference extends Reference {
1818
* @param identifier - The include identifier (the path passed to include(), which should resolve to a file).
1919
* @param inScope - The parent scope which the include should be linked to.
2020
* @param outScope - The scope in which the include should be usable.
21-
* @param t - The target of the declaration.
21+
* @param t - The target of the reference.
2222
*/
2323
public IncludeReference(String identifier, Scope inScope, Scope outScope, Target t) {
2424
super(Namespace.INCLUDE, identifier, t);

src/main/java/com/laytonsmith/core/compiler/analysis/Namespace.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public enum Namespace {
99
IVARIABLE,
1010
IVARIABLE_ASSIGN,
1111
PROCEDURE,
12-
INCLUDE
12+
INCLUDE,
13+
RETURNABLE
1314
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.laytonsmith.core.compiler.analysis;
2+
3+
import com.laytonsmith.core.NodeModifiers;
4+
import com.laytonsmith.core.constructs.CClassType;
5+
import com.laytonsmith.core.constructs.Target;
6+
7+
/**
8+
* Represents a returnable declaration in a scope graph.
9+
* @author P.J.S. Kools
10+
*/
11+
public class ReturnableDeclaration extends Declaration {
12+
13+
/**
14+
* Creates a new {@link ReturnableDeclaration} in the {@link Namespace#RETURNABLE} namespace.
15+
* @param type The expected return {@link CClassType}.
16+
* @param modifiers The node modifiers.
17+
* @param t The returnable target.
18+
*/
19+
public ReturnableDeclaration(CClassType type, NodeModifiers modifiers, Target t) {
20+
super(Namespace.RETURNABLE, null, type, modifiers, t);
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.laytonsmith.core.compiler.analysis;
2+
3+
import com.laytonsmith.core.constructs.Target;
4+
5+
/**
6+
* Represents a reference to a returnable in a scope graph.
7+
* @author P.J.S. Kools
8+
*/
9+
public class ReturnableReference extends Reference {
10+
11+
/**
12+
* Creates a new {@link ReturnableReference} in the {@link Namespace#RETURNABLE} scope.
13+
* @param t - The target of the reference.
14+
*/
15+
public ReturnableReference(Target t) {
16+
super(Namespace.RETURNABLE, null, t);
17+
}
18+
}

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import com.laytonsmith.core.compiler.FileOptions;
2424
import com.laytonsmith.core.compiler.SelfStatement;
2525
import com.laytonsmith.core.compiler.VariableScope;
26+
import com.laytonsmith.core.compiler.analysis.Declaration;
27+
import com.laytonsmith.core.compiler.analysis.Namespace;
28+
import com.laytonsmith.core.compiler.analysis.ReturnableReference;
2629
import com.laytonsmith.core.compiler.analysis.Scope;
2730
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
2831
import com.laytonsmith.core.compiler.signature.FunctionSignatures;
@@ -2411,11 +2414,66 @@ public String docs() {
24112414
+ " see the docs on try/catch, particularly the finally clause for example).";
24122415
}
24132416

2417+
@Override
2418+
public CClassType typecheck(StaticAnalysis analysis,
2419+
ParseTree ast, Environment env, Set<ConfigCompileException> exceptions) {
2420+
2421+
// Get value type.
2422+
CClassType valType;
2423+
Target valTarget;
2424+
if(ast.numberOfChildren() == 0) {
2425+
valType = CVoid.TYPE;
2426+
valTarget = ast.getTarget();
2427+
} else if(ast.numberOfChildren() == 1) {
2428+
ParseTree valNode = ast.getChildAt(0);
2429+
valType = analysis.typecheck(valNode, env, exceptions);
2430+
valTarget = valNode.getTarget();
2431+
} else {
2432+
2433+
// Fall back to default behavior for invalid usage.
2434+
return super.typecheck(analysis, ast, env, exceptions);
2435+
}
2436+
2437+
// Resolve this returnable reference to its returnable declaration to get its required return type.
2438+
Scope scope = analysis.getTermScope(ast);
2439+
if(scope != null) {
2440+
Set<Declaration> decls = scope.getDeclarations(Namespace.RETURNABLE, null);
2441+
if(decls.size() == 0) {
2442+
exceptions.add(new ConfigCompileException("Return is not valid in this context.", ast.getTarget()));
2443+
} else {
2444+
2445+
// Type check return value for all found declared return types.
2446+
for(Declaration decl : decls) {
2447+
StaticAnalysis.requireType(valType, decl.getType(), valTarget, env, exceptions);
2448+
}
2449+
}
2450+
}
2451+
2452+
// Return void.
2453+
return CVoid.TYPE;
2454+
}
2455+
24142456
@Override
24152457
public Class<? extends CREThrowable>[] thrown() {
24162458
return null;
24172459
}
24182460

2461+
@Override
2462+
public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast,
2463+
Environment env, Set<ConfigCompileException> exceptions) {
2464+
2465+
// Handle children. These will execute before this return().
2466+
Scope scope = super.linkScope(analysis, parentScope, ast, env, exceptions);
2467+
2468+
// Add returnable reference in new scope.
2469+
scope = analysis.createNewScope(scope);
2470+
scope.addReference(new ReturnableReference(ast.getTarget()));
2471+
analysis.setTermScope(ast, scope);
2472+
2473+
// Return scope.
2474+
return scope;
2475+
}
2476+
24192477
@Override
24202478
public boolean isRestricted() {
24212479
return false;

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.laytonsmith.core.compiler.analysis.ProcDeclaration;
4141
import com.laytonsmith.core.compiler.analysis.ProcRootDeclaration;
4242
import com.laytonsmith.core.compiler.analysis.Reference;
43+
import com.laytonsmith.core.compiler.analysis.ReturnableDeclaration;
4344
import com.laytonsmith.core.compiler.analysis.Scope;
4445
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
4546
import com.laytonsmith.core.compiler.signature.FunctionSignatures;
@@ -1626,6 +1627,9 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast
16261627
// Allow procedures to perform lookups in the decl scope.
16271628
paramScope.addSpecificParent(declScope, Namespace.PROCEDURE);
16281629

1630+
// Create returnable declaration in the inner root scope.
1631+
paramScope.addDeclaration(new ReturnableDeclaration(retType, ast.getNodeModifiers(), ast.getTarget()));
1632+
16291633
// Return the declaration scope. Parameters and their default values are not accessible after the procedure.
16301634
return declScope;
16311635
}
@@ -2703,10 +2707,16 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast
27032707
return parentScope;
27042708
}
27052709

2706-
// Handle optional return type argument.
2710+
// Handle optional return type argument (CClassType or CVoid, default to AUTO).
27072711
int ind = 0;
2708-
if(ast.getChildAt(ind).getData().isInstanceOf(CClassType.TYPE)) {
2709-
analysis.linkScope(parentScope, ast.getChildAt(ind++), env, exceptions);
2712+
CClassType retType;
2713+
if(ast.getChildAt(ind).getData() instanceof CClassType) {
2714+
retType = (CClassType) ast.getChildAt(ind++).getData();
2715+
} else if(ast.getChildAt(ind).getData().equals(CVoid.VOID)) {
2716+
ind++;
2717+
retType = CVoid.TYPE;
2718+
} else {
2719+
retType = CClassType.AUTO;
27102720
}
27112721

27122722
// Create parameter scope. Set parent scope if this closure type is allowed to resolve in the parent scope.
@@ -2732,6 +2742,9 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope, ParseTree ast
27322742
paramScope = scopes[0];
27332743
}
27342744

2745+
// Create returnable declaration in the inner root scope.
2746+
paramScope.addDeclaration(new ReturnableDeclaration(retType, ast.getNodeModifiers(), ast.getTarget()));
2747+
27352748
// Handle closure code.
27362749
ParseTree code = ast.getChildAt(ast.numberOfChildren() - 1);
27372750
analysis.linkScope(paramScope, code, env, exceptions);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.laytonsmith.core.compiler.SelfStatement;
2020
import com.laytonsmith.core.compiler.VariableScope;
2121
import com.laytonsmith.core.compiler.analysis.Namespace;
22+
import com.laytonsmith.core.compiler.analysis.ReturnableDeclaration;
2223
import com.laytonsmith.core.compiler.analysis.Scope;
2324
import com.laytonsmith.core.compiler.analysis.StaticAnalysis;
2425
import com.laytonsmith.core.constructs.CArray;
@@ -337,6 +338,9 @@ public Scope linkScope(StaticAnalysis analysis, Scope parentScope,
337338
}
338339
analysis.linkScope(paramScope, code, env, exceptions);
339340

341+
// Create returnable declaration in the inner root scope.
342+
paramScope.addDeclaration(new ReturnableDeclaration(CVoid.TYPE, ast.getNodeModifiers(), ast.getTarget()));
343+
340344
// Allow code after bind() to access declarations in assigned values, but not parameters themselves.
341345
return valScope;
342346
}

0 commit comments

Comments
 (0)