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:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user