feat: defer 延迟执行 — defer { stmts; } 在 return 前按 LIFO 执行

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 13:51:10 +08:00
parent e60021b684
commit 0088347576
10 changed files with 62 additions and 6 deletions
+6
View File
@@ -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;
+4
View File
@@ -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);
+16 -3
View File
@@ -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);
+2
View File
@@ -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;
// === 类型映射 ===
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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] = "浮点数",
+1 -1
View File
@@ -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,
+21
View File
@@ -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;
+3
View File
@@ -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);