...
This commit is contained in:
@@ -22,8 +22,4 @@ public record ContinueNode : TerminalStatementNode;
|
||||
|
||||
public record BreakNode : TerminalStatementNode;
|
||||
|
||||
public record DeferNode(StatementNode Statement) : StatementNode;
|
||||
|
||||
public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode;
|
||||
|
||||
public record ForArrayNode(NubArrayType ArrayType, string ElementIdent, string? IndexIdent, ExpressionNode Target, BlockNode Body) : StatementNode;
|
||||
public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode;
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using NubLang.Diagnostics;
|
||||
using NubLang.Modules;
|
||||
@@ -125,7 +126,7 @@ public sealed class TypeChecker
|
||||
}
|
||||
|
||||
_funcReturnTypes.Push(ResolveType(function.Signature.ReturnType));
|
||||
var body = CheckBlock(function.Body, CurrentScope);
|
||||
var body = CheckBlock(function.Body);
|
||||
_funcReturnTypes.Pop();
|
||||
return new StructFuncNode(function.Name, function.Hook, CheckFuncSignature(function.Signature), body);
|
||||
}
|
||||
@@ -143,10 +144,9 @@ public sealed class TypeChecker
|
||||
|
||||
private FuncNode CheckFuncDefinition(FuncSyntax node)
|
||||
{
|
||||
var scope = new Scope();
|
||||
foreach (var parameter in node.Signature.Parameters)
|
||||
{
|
||||
scope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue));
|
||||
CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue));
|
||||
}
|
||||
|
||||
var signature = CheckFuncSignature(node.Signature);
|
||||
@@ -156,7 +156,7 @@ public sealed class TypeChecker
|
||||
{
|
||||
_funcReturnTypes.Push(signature.ReturnType);
|
||||
|
||||
body = CheckBlock(node.Body, scope);
|
||||
body = CheckBlock(node.Body);
|
||||
|
||||
if (!AlwaysReturns(body))
|
||||
{
|
||||
@@ -179,24 +179,6 @@ public sealed class TypeChecker
|
||||
return new FuncNode(CurrentModule, node.Name, node.ExternSymbol, signature, body);
|
||||
}
|
||||
|
||||
private StatementNode CheckStatement(StatementSyntax node)
|
||||
{
|
||||
return node switch
|
||||
{
|
||||
AssignmentSyntax statement => CheckAssignment(statement),
|
||||
BreakSyntax => new BreakNode(),
|
||||
ContinueSyntax => new ContinueNode(),
|
||||
IfSyntax statement => CheckIf(statement),
|
||||
ReturnSyntax statement => CheckReturn(statement),
|
||||
StatementExpressionSyntax statement => CheckStatementExpression(statement),
|
||||
VariableDeclarationSyntax statement => CheckVariableDeclaration(statement),
|
||||
DeferSyntax statement => CheckDefer(statement),
|
||||
WhileSyntax statement => CheckWhile(statement),
|
||||
ForSyntax statement => CheckFor(statement),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
||||
};
|
||||
}
|
||||
|
||||
private AssignmentNode CheckAssignment(AssignmentSyntax statement)
|
||||
{
|
||||
var target = CheckExpression(statement.Target);
|
||||
@@ -272,11 +254,6 @@ public sealed class TypeChecker
|
||||
return new VariableDeclarationNode(statement.Name, Optional.OfNullable(assignmentNode), type);
|
||||
}
|
||||
|
||||
private DeferNode CheckDefer(DeferSyntax statement)
|
||||
{
|
||||
return new DeferNode(CheckStatement(statement.Statement));
|
||||
}
|
||||
|
||||
private WhileNode CheckWhile(WhileSyntax statement)
|
||||
{
|
||||
var condition = CheckExpression(statement.Condition, new NubBoolType());
|
||||
@@ -284,31 +261,6 @@ public sealed class TypeChecker
|
||||
return new WhileNode(condition, body);
|
||||
}
|
||||
|
||||
private StatementNode CheckFor(ForSyntax statement)
|
||||
{
|
||||
var target = CheckExpression(statement.Target);
|
||||
|
||||
switch (target.Type)
|
||||
{
|
||||
case NubArrayType arrayType:
|
||||
{
|
||||
var scope = CurrentScope.SubScope();
|
||||
scope.DeclareVariable(new Variable(statement.ElementIdent, arrayType.ElementType, VariableKind.RValue));
|
||||
if (statement.IndexIdent != null)
|
||||
{
|
||||
scope.DeclareVariable(new Variable(statement.ElementIdent, new NubIntType(true, 64), VariableKind.RValue));
|
||||
}
|
||||
|
||||
var body = CheckBlock(statement.Body, scope);
|
||||
return new ForArrayNode(arrayType, statement.ElementIdent, statement.IndexIdent, target, body);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new TypeCheckerException(Diagnostic.Error($"Type {target.Type} is not an iterable target").At(statement.Target).Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax statement)
|
||||
{
|
||||
var parameters = new List<FuncParameterNode>();
|
||||
@@ -802,42 +754,98 @@ public sealed class TypeChecker
|
||||
return new StructInitializerNode(structType, initializers);
|
||||
}
|
||||
|
||||
private BlockNode CheckBlock(BlockSyntax node, Scope? scope = null)
|
||||
private BlockNode CheckBlock(BlockSyntax node)
|
||||
{
|
||||
var statements = new List<StatementNode>();
|
||||
|
||||
_scopes.Push(scope ?? CurrentScope.SubScope());
|
||||
|
||||
var reachable = true;
|
||||
var warnedUnreachable = false;
|
||||
|
||||
BeginScope(false);
|
||||
|
||||
foreach (var statement in node.Statements)
|
||||
{
|
||||
var checkedStatement = CheckStatement(statement);
|
||||
|
||||
if (reachable)
|
||||
if (statement is DeferSyntax deferStmt)
|
||||
{
|
||||
statements.Add(checkedStatement);
|
||||
if (checkedStatement is TerminalStatementNode)
|
||||
{
|
||||
reachable = false;
|
||||
}
|
||||
CurrentScope.PushDefer(deferStmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!warnedUnreachable)
|
||||
var checkedStatement = CheckStatement(statement);
|
||||
|
||||
if (reachable)
|
||||
{
|
||||
Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build());
|
||||
warnedUnreachable = true;
|
||||
if (checkedStatement is TerminalStatementNode)
|
||||
{
|
||||
while (CurrentScope.TryPopDefer(out var defer))
|
||||
{
|
||||
var checkedDeferredStatement = CheckStatement(defer.Statement);
|
||||
if (checkedDeferredStatement is TerminalStatementNode)
|
||||
{
|
||||
throw new TypeCheckerException(Diagnostic
|
||||
.Error("Cannot defer terminal statements")
|
||||
.WithHelp("Avoid using defer with terminal statements such as 'return', 'break', 'continue' etc.")
|
||||
.At(defer)
|
||||
.Build());
|
||||
}
|
||||
|
||||
statements.Add(checkedDeferredStatement);
|
||||
}
|
||||
|
||||
reachable = false;
|
||||
}
|
||||
|
||||
statements.Add(checkedStatement);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!warnedUnreachable)
|
||||
{
|
||||
Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build());
|
||||
warnedUnreachable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_scopes.Pop();
|
||||
while (CurrentScope.TryPopDefer(out var defer))
|
||||
{
|
||||
var checkedDeferredStatement = CheckStatement(defer.Statement);
|
||||
if (checkedDeferredStatement is TerminalStatementNode)
|
||||
{
|
||||
throw new TypeCheckerException(Diagnostic
|
||||
.Error("Cannot defer terminal statements")
|
||||
.WithHelp("Avoid using defer with terminal statements such as 'return', 'break', 'continue' etc.")
|
||||
.At(defer)
|
||||
.Build());
|
||||
}
|
||||
|
||||
statements.Add(checkedDeferredStatement);
|
||||
}
|
||||
|
||||
EndScope();
|
||||
|
||||
return new BlockNode(statements);
|
||||
}
|
||||
|
||||
private StatementNode CheckStatement(StatementSyntax statement)
|
||||
{
|
||||
return statement switch
|
||||
{
|
||||
AssignmentSyntax assignmentStmt => CheckAssignment(assignmentStmt),
|
||||
BlockSyntax blockStmt => CheckBlock(blockStmt),
|
||||
BreakSyntax => new BreakNode(),
|
||||
ContinueSyntax => new ContinueNode(),
|
||||
IfSyntax ifStmt => CheckIf(ifStmt),
|
||||
ReturnSyntax retStmt => CheckReturn(retStmt),
|
||||
StatementExpressionSyntax stmtExpr => CheckStatementExpression(stmtExpr),
|
||||
VariableDeclarationSyntax varDeclStmt => CheckVariableDeclaration(varDeclStmt),
|
||||
WhileSyntax whileStmt => CheckWhile(whileStmt),
|
||||
DeferSyntax => throw new InvalidOperationException($"Compiler bug: defer statement should not have reached {nameof(CheckStatement)}"),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(statement))
|
||||
};
|
||||
}
|
||||
|
||||
private bool AlwaysReturns(StatementNode statement)
|
||||
{
|
||||
switch (statement)
|
||||
@@ -949,6 +957,7 @@ public record Variable(string Name, NubType Type, VariableKind Kind);
|
||||
|
||||
public class Scope(Scope? parent = null)
|
||||
{
|
||||
private readonly Stack<DeferSyntax> _defers = [];
|
||||
private readonly List<Variable> _variables = [];
|
||||
|
||||
public Variable? LookupVariable(string name)
|
||||
@@ -967,6 +976,16 @@ public class Scope(Scope? parent = null)
|
||||
_variables.Add(variable);
|
||||
}
|
||||
|
||||
public void PushDefer(DeferSyntax defer)
|
||||
{
|
||||
_defers.Push(defer);
|
||||
}
|
||||
|
||||
public bool TryPopDefer([NotNullWhen(true)] out DeferSyntax? defer)
|
||||
{
|
||||
return _defers.TryPop(out defer);
|
||||
}
|
||||
|
||||
public Scope SubScope()
|
||||
{
|
||||
return new Scope(this);
|
||||
|
||||
Reference in New Issue
Block a user