From 511f0d27ee80f2bb9fb754fdb87b3b11e68a3c94 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sat, 20 Sep 2025 19:59:48 +0200 Subject: [PATCH] defers --- .../NubLang/Generation/QBE/QBEGenerator.cs | 9 +++ .../TypeChecking/Node/StatementNode.cs | 4 +- compiler/NubLang/TypeChecking/TypeChecker.cs | 75 ++++--------------- 3 files changed, 25 insertions(+), 63 deletions(-) diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 32fa854..a926f5f 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -498,6 +498,9 @@ public class QBEGenerator EmitScopeCleanup(); _writer.Indented($"jmp {_continueLabels.Peek()}"); break; + case DeferNode defer: + Scope.Defers.Push(defer); + break; case IfNode ifStatement: EmitIf(ifStatement); break; @@ -523,6 +526,11 @@ public class QBEGenerator private void EmitScopeCleanup() { + while (Scope.Defers.TryPop(out var defer)) + { + EmitStatement(defer.Statement); + } + while (Scope.Variables.TryPop(out var variable)) { if (variable.Type is NubStructType structType) @@ -1283,6 +1291,7 @@ public class QBEGenerator public class Scope { public readonly Stack Variables = []; + public readonly Stack Defers = []; } public record Variable(string Name, NubType Type); diff --git a/compiler/NubLang/TypeChecking/Node/StatementNode.cs b/compiler/NubLang/TypeChecking/Node/StatementNode.cs index 363e7c0..e45d9ac 100644 --- a/compiler/NubLang/TypeChecking/Node/StatementNode.cs +++ b/compiler/NubLang/TypeChecking/Node/StatementNode.cs @@ -22,4 +22,6 @@ public record ContinueNode : TerminalStatementNode; public record BreakNode : TerminalStatementNode; -public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; \ No newline at end of file +public record WhileNode(ExpressionNode Condition, BlockNode Body) : StatementNode; + +public record DeferNode(StatementNode Statement) : StatementNode; \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index d7961a6..7930f83 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using NubLang.Diagnostics; using NubLang.Modules; @@ -765,64 +764,27 @@ public sealed class TypeChecker foreach (var statement in node.Statements) { - if (statement is DeferSyntax deferStmt) + var checkedStatement = CheckStatement(statement); + + if (reachable) { - CurrentScope.PushDefer(deferStmt); + if (checkedStatement is TerminalStatementNode) + { + reachable = false; + } + + statements.Add(checkedStatement); } else { - var checkedStatement = CheckStatement(statement); - - if (reachable) + if (!warnedUnreachable) { - 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; - } + Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build()); + warnedUnreachable = true; } } } - 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); @@ -841,7 +803,7 @@ public sealed class TypeChecker 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)}"), + DeferSyntax defer => new DeferNode(CheckStatement(defer.Statement)), _ => throw new ArgumentOutOfRangeException(nameof(statement)) }; } @@ -957,7 +919,6 @@ public record Variable(string Name, NubType Type, VariableKind Kind); public class Scope(Scope? parent = null) { - private readonly Stack _defers = []; private readonly List _variables = []; public Variable? LookupVariable(string name) @@ -976,16 +937,6 @@ 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);