From 00883475762ee34cb2c27aa4e4c611f3f6b6f0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sun, 7 Jun 2026 13:51:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20defer=20=E5=BB=B6=E8=BF=9F=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=20=E2=80=94=20defer=20{=20stmts;=20}=20=E5=9C=A8=20re?= =?UTF-8?q?turn=20=E5=89=8D=E6=8C=89=20LIFO=20=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Token(73): +TOK_DEFER, AST(28): +AST_DEFER_STMT, 新增 38_defer.l parser: defer { ... } 块 + defer expr; 表达式两种形式 codegen: defer 栈压入 block, emit_deferred() 在 return 前 LIFO 发射 Co-Authored-By: Claude Opus 4.7 --- src/ast/ast.c | 6 ++++++ src/ast/ast.h | 4 ++++ src/codegen/codegen.c | 19 ++++++++++++++++--- src/codegen/codegen_internal.h | 2 ++ src/lexer/lexer.c | 2 +- src/lexer/token.c | 2 +- src/lexer/token.h | 2 +- src/parser/parser.c | 21 +++++++++++++++++++++ src/sema/sema.c | 3 +++ test/programs/38_defer.l | 7 +++++++ 10 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 test/programs/38_defer.l diff --git a/src/ast/ast.c b/src/ast/ast.c index 3730d54..ae2ece6 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -103,6 +103,12 @@ AstNode* ast_make_expr_stmt(void* alloc, AstNode* expr, SourceLoc loc) { return n; } +AstNode* ast_make_defer_stmt(void* alloc, AstNode* body, SourceLoc loc) { + NEW(alloc, AST_DEFER_STMT); + n->as.defer_stmt.body = body; + return n; +} + AstNode* ast_make_binary(void* alloc, BinaryOp op, AstNode* left, AstNode* right, SourceLoc loc) { NEW(alloc, AST_BINARY_EXPR); n->as.binary.op = op; n->as.binary.left = left; n->as.binary.right = right; diff --git a/src/ast/ast.h b/src/ast/ast.h index 74907eb..269bb15 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -15,6 +15,7 @@ typedef enum { AST_WHILE_STMT, AST_RETURN_STMT, AST_EXPR_STMT, + AST_DEFER_STMT, AST_BINARY_EXPR, AST_UNARY_EXPR, AST_CALL_EXPR, @@ -88,6 +89,8 @@ struct AstNode { struct { struct AstNode* expr; } return_stmt; // AST_EXPR_STMT struct { struct AstNode* expr; } expr_stmt; + // AST_DEFER_STMT + struct { struct AstNode* body; } defer_stmt; // AST_BINARY_EXPR struct { BinaryOp op; struct AstNode* left; struct AstNode* right; } binary; // AST_UNARY_EXPR @@ -153,6 +156,7 @@ AstNode* ast_make_if(void* alloc, AstNode* cond, AstNode* then_b, AstNode* else_ AstNode* ast_make_while(void* alloc, AstNode* cond, AstNode* body, SourceLoc loc); AstNode* ast_make_return(void* alloc, AstNode* expr, SourceLoc loc); AstNode* ast_make_expr_stmt(void* alloc, AstNode* expr, SourceLoc loc); +AstNode* ast_make_defer_stmt(void* alloc, AstNode* expr, SourceLoc loc); AstNode* ast_make_binary(void* alloc, BinaryOp op, AstNode* left, AstNode* right, SourceLoc loc); AstNode* ast_make_unary(void* alloc, BinaryOp op, AstNode* operand, SourceLoc loc); AstNode* ast_make_call(void* alloc, const char* name, AstNode** args, const char** arg_names, size_t count, SourceLoc loc); diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 417fc6b..7db4f4e 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -77,6 +77,13 @@ void cleanup_emit(CgCtx* ctx, size_t from_mark) { ctx->cleanup_count = from_mark; } +// 发射所有 defer 语句(LIFO 顺序) +static void emit_deferred(CgCtx* ctx) { + for (size_t i = ctx->defer_count; i > 0; i--) + codegen_stmt(ctx, ctx->defer_exprs[i - 1]); + ctx->defer_count = 0; +} + // === 语句代码生成 === void codegen_stmt(CgCtx* ctx, AstNode* node) { if (!node) return; @@ -146,6 +153,11 @@ void codegen_stmt(CgCtx* ctx, AstNode* node) { codegen_expr(ctx, node->as.expr_stmt.expr); break; + case AST_DEFER_STMT: + if (ctx->defer_count < 64) + ctx->defer_exprs[ctx->defer_count++] = node->as.defer_stmt.body; + break; + case AST_RETURN_STMT: { // 先计算返回值 LLVMValueRef ret_val = NULL; @@ -168,9 +180,9 @@ void codegen_stmt(CgCtx* ctx, AstNode* node) { } } } - // return 前释放当前作用域所有 str 堆分配 + // defer → cleanup → ret 的顺序 + emit_deferred(ctx); cleanup_emit(ctx, 0); - // 然后 emit ret if (has_val) LLVMBuildRet(ctx->builder, ret_val); else LLVMBuildRetVoid(ctx->builder); break; @@ -416,11 +428,12 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena, add_var(&ctx, pnode->as.parameter.name, alloca, param_ty); } + ctx.defer_count = 0; codegen_stmt(&ctx, fn->as.function.body); // 确保函数有终止指令(terminator) if (!LLVMGetBasicBlockTerminator(LLVMGetInsertBlock(ctx.builder))) { - // 函数结尾隐式 return: 先释放所有 str 堆分配 + emit_deferred(&ctx); cleanup_emit(&ctx, 0); if (fn->as.function.return_type == TYPE_VOID) LLVMBuildRetVoid(ctx.builder); diff --git a/src/codegen/codegen_internal.h b/src/codegen/codegen_internal.h index 757ba07..0667119 100644 --- a/src/codegen/codegen_internal.h +++ b/src/codegen/codegen_internal.h @@ -57,6 +57,8 @@ typedef struct { LLVMValueRef* cleanup_list; size_t cleanup_count; size_t cleanup_cap; + AstNode* defer_exprs[64]; + size_t defer_count; } CgCtx; // === 类型映射 === diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 28a3722..eb4b81e 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -65,7 +65,7 @@ static TokenKind check_keyword(const Token* tok) { KW("bool", TOK_BOOL); KW("char", TOK_CHAR); KW("str", TOK_STR); KW("void", TOK_VOID); KW("struct", TOK_STRUCT); KW("type", TOK_TYPE); - KW("enum", TOK_ENUM); KW("extend", TOK_EXTEND); KW("match", TOK_MATCH); + KW("enum", TOK_ENUM); KW("extend", TOK_EXTEND); KW("defer", TOK_DEFER); KW("match", TOK_MATCH); KW("pub", TOK_PUB); KW("mod", TOK_MOD); KW("use", TOK_USE); KW("trait", TOK_TRAIT); KW("Self", TOK_SELF); KW("_", TOK_UNDERSCORE); diff --git a/src/lexer/token.c b/src/lexer/token.c index 7779244..ef77300 100644 --- a/src/lexer/token.c +++ b/src/lexer/token.c @@ -10,7 +10,7 @@ static const char* NAMES[] = { [TOK_TRAIT] = "trait", [TOK_SELF] = "Self", [TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_FOR] = "for", [TOK_IN] = "in", [TOK_RETURN] = "return", [TOK_STRUCT] = "struct", [TOK_TYPE] = "type", [TOK_ENUM] = "enum", [TOK_EXTEND] = "extend", - [TOK_MATCH] = "match", + [TOK_DEFER] = "defer", [TOK_MATCH] = "match", [TOK_I32] = "i32", [TOK_I64] = "i64", [TOK_U64] = "u64", [TOK_F64] = "f64", [TOK_BOOL] = "bool", [TOK_CHAR] = "char", [TOK_STR] = "str", [TOK_VOID] = "void", [TOK_INT_LIT] = "整数", [TOK_FLOAT_LIT] = "浮点数", diff --git a/src/lexer/token.h b/src/lexer/token.h index 5b3c81f..0682065 100644 --- a/src/lexer/token.h +++ b/src/lexer/token.h @@ -7,7 +7,7 @@ typedef enum { // 关键字 TOK_FN, TOK_LET, TOK_VAR, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_IN, TOK_RETURN, TOK_GUARD, - TOK_STRUCT, TOK_TYPE, TOK_ENUM, TOK_EXTEND, TOK_MATCH, TOK_PUB, TOK_MOD, TOK_USE, + TOK_STRUCT, TOK_TYPE, TOK_ENUM, TOK_EXTEND, TOK_DEFER, TOK_MATCH, TOK_PUB, TOK_MOD, TOK_USE, TOK_TRAIT, TOK_SELF, // 类型关键字 TOK_I32, TOK_I64, TOK_U64, TOK_F64, TOK_BOOL, TOK_CHAR, TOK_STR, TOK_VOID, diff --git a/src/parser/parser.c b/src/parser/parser.c index 2d330ab..f2fc057 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -204,6 +204,27 @@ AstNode* parse_statement(Parser* p, ErrorInfo* error) { return ast_make_return(p->arena, expr, tok_loc(t)); } + if (t->kind == TOK_DEFER) { + advance(p); + AstNode* body; + if (peek(p)->kind == TOK_LBRACE) { + body = parse_block(p, error); + } else { + AstNode* expr = parse_expr(p, error); + if (!expr) return NULL; + if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL; + body = ast_make_block(p->arena, + arena_alloc_impl(p->arena, sizeof(AstNode*)), + 0, tok_loc(t)); + AstNode** stmts = arena_alloc_impl(p->arena, sizeof(AstNode*)); + stmts[0] = ast_make_expr_stmt(p->arena, expr, tok_loc(t)); + body->as.block.stmts = stmts; + body->as.block.stmt_count = 1; + } + if (!body) return NULL; + return ast_make_defer_stmt(p->arena, body, tok_loc(t)); + } + // 数组元素赋值: ident[expr] = expr ; if (t->kind == TOK_IDENT && (t + 1)->kind == TOK_LBRACKET) { int ahead_idx = 2; diff --git a/src/sema/sema.c b/src/sema/sema.c index 7f64bbd..e1f212c 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -472,6 +472,9 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { case AST_EXPR_STMT: analyze_expr(node->as.expr_stmt.expr, scope, errors, a); break; + case AST_DEFER_STMT: + analyze_node(node->as.defer_stmt.body, scope, errors, a); + break; default: analyze_expr(node, scope, errors, a); diff --git a/test/programs/38_defer.l b/test/programs/38_defer.l new file mode 100644 index 0000000..fe36bd5 --- /dev/null +++ b/test/programs/38_defer.l @@ -0,0 +1,7 @@ +fn main() -> i64 { + var x = 10; + defer { print_str("deferred"); } + print_str("normal"); + print_i64(x); + return 0; +}