astgen: catch error capture, @panic, @errorName, @field, larger block scopes
- Add error capture scope to orelseCatchExpr (catch |err| now creates a ScopeLocalVal for the captured error variable) - Add @panic, @errorName, @field builtins - Increase blockExprStmts scope arrays from 64 to 128 entries (build.zig has 93 var decls in a single block) corpus build.zig still skipped: needs switchExprErrUnion optimization (catch |err| switch(err) pattern). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
106
stage0/astgen.c
106
stage0/astgen.c
@@ -2346,7 +2346,7 @@ static uint32_t forExpr(
|
||||
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_statement);
|
||||
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
||||
uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op,
|
||||
ZirInstTag unwrap_code_op);
|
||||
ZirInstTag unwrap_code_op, uint32_t payload_token);
|
||||
static uint32_t arrayInitDotExpr(
|
||||
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
||||
static uint32_t switchExpr(
|
||||
@@ -2475,6 +2475,7 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
|
||||
#define COMPTIME_REASON_ARRAY_LENGTH 33
|
||||
#define COMPTIME_REASON_ALIGN 50
|
||||
#define COMPTIME_REASON_ADDRSPACE 51
|
||||
#define COMPTIME_REASON_FIELD_NAME 42
|
||||
#define COMPTIME_REASON_COMPTIME_KEYWORD 53
|
||||
#define COMPTIME_REASON_SWITCH_ITEM 56
|
||||
#define COMPTIME_REASON_TUPLE_FIELD_DEFAULT_VALUE 57
|
||||
@@ -2923,6 +2924,51 @@ static uint32_t builtinCall(
|
||||
return rvalue(gz, rl,
|
||||
addPlNodeBin(gz, ZIR_INST_MAX, node, a, b), node);
|
||||
}
|
||||
// @panic — simpleUnOp with dbg_node (AstGen.zig:9429-9432).
|
||||
if (name_len == 5 && memcmp(source + name_start, "panic", 5) == 0) {
|
||||
emitDbgNode(gz, node);
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
ResultLoc panic_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_SLICE_CONST_U8_TYPE,
|
||||
.src_node = 0, .ctx = RI_CTX_NONE };
|
||||
uint32_t operand = exprRl(gz, scope, panic_rl, nd.lhs);
|
||||
uint32_t result = addUnNode(gz, ZIR_INST_PANIC, operand, node);
|
||||
return rvalue(gz, rl, result, node);
|
||||
}
|
||||
// @errorName — simpleUnOp with dbg_stmt (AstGen.zig:9391).
|
||||
if (name_len == 9 && memcmp(source + name_start, "errorName", 9) == 0) {
|
||||
advanceSourceCursorToMainToken(ag, gz, node);
|
||||
uint32_t saved_line = ag->source_line - gz->decl_line;
|
||||
uint32_t saved_col = ag->source_column;
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
ResultLoc err_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_ANYERROR_TYPE,
|
||||
.src_node = 0, .ctx = RI_CTX_NONE };
|
||||
uint32_t operand = exprRl(gz, scope, err_rl, nd.lhs);
|
||||
emitDbgStmt(gz, saved_line, saved_col);
|
||||
uint32_t result = addUnNode(gz, ZIR_INST_ERROR_NAME, operand, node);
|
||||
return rvalue(gz, rl, result, node);
|
||||
}
|
||||
// @field (AstGen.zig:9288-9300).
|
||||
if (name_len == 5 && memcmp(source + name_start, "field", 5) == 0) {
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
ResultLoc field_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_SLICE_CONST_U8_TYPE,
|
||||
.src_node = 0, .ctx = RI_CTX_NONE };
|
||||
if (RL_IS_REF(rl)) {
|
||||
uint32_t lhs = exprRl(gz, scope, RL_REF_VAL, nd.lhs);
|
||||
uint32_t fname = comptimeExpr(
|
||||
gz, scope, field_rl, nd.rhs, COMPTIME_REASON_FIELD_NAME);
|
||||
return addPlNodeBin(
|
||||
gz, ZIR_INST_FIELD_PTR_NAMED, node, lhs, fname);
|
||||
}
|
||||
uint32_t lhs = expr(gz, scope, nd.lhs);
|
||||
uint32_t fname = comptimeExpr(
|
||||
gz, scope, field_rl, nd.rhs, COMPTIME_REASON_FIELD_NAME);
|
||||
uint32_t result = addPlNodeBin(
|
||||
gz, ZIR_INST_FIELD_VAL_NAMED, node, lhs, fname);
|
||||
return rvalue(gz, rl, result, node);
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// TODO: handle other builtins.
|
||||
@@ -4916,21 +4962,27 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
if (RL_IS_REF(rl)) {
|
||||
return orelseCatchExpr(gz, scope, rl, node,
|
||||
ZIR_INST_IS_NON_NULL_PTR, ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE_PTR,
|
||||
(ZirInstTag)0);
|
||||
(ZirInstTag)0, UINT32_MAX);
|
||||
} else {
|
||||
return orelseCatchExpr(gz, scope, rl, node, ZIR_INST_IS_NON_NULL,
|
||||
ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE, (ZirInstTag)0);
|
||||
ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE, (ZirInstTag)0, UINT32_MAX);
|
||||
}
|
||||
// catch (AstGen.zig:1017-1052).
|
||||
case AST_NODE_CATCH:
|
||||
case AST_NODE_CATCH: {
|
||||
uint32_t catch_token = ag->tree->nodes.main_tokens[node];
|
||||
uint32_t pt = (ag->tree->tokens.tags[catch_token + 1] == TOKEN_PIPE)
|
||||
? catch_token + 2
|
||||
: UINT32_MAX;
|
||||
if (RL_IS_REF(rl)) {
|
||||
return orelseCatchExpr(gz, scope, rl, node,
|
||||
ZIR_INST_IS_NON_ERR_PTR, ZIR_INST_ERR_UNION_PAYLOAD_UNSAFE_PTR,
|
||||
ZIR_INST_ERR_UNION_CODE_PTR);
|
||||
ZIR_INST_ERR_UNION_CODE_PTR, pt);
|
||||
} else {
|
||||
return orelseCatchExpr(gz, scope, rl, node, ZIR_INST_IS_NON_ERR,
|
||||
ZIR_INST_ERR_UNION_PAYLOAD_UNSAFE, ZIR_INST_ERR_UNION_CODE);
|
||||
ZIR_INST_ERR_UNION_PAYLOAD_UNSAFE, ZIR_INST_ERR_UNION_CODE,
|
||||
pt);
|
||||
}
|
||||
}
|
||||
// Block expressions (AstGen.zig:984-992).
|
||||
case AST_NODE_BLOCK_TWO:
|
||||
case AST_NODE_BLOCK_TWO_SEMICOLON:
|
||||
@@ -6220,10 +6272,7 @@ static uint32_t forExpr(
|
||||
|
||||
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
||||
uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op,
|
||||
ZirInstTag unwrap_code_op) {
|
||||
// unwrap_code_op used for catch payload capture scope (not yet
|
||||
// implemented in C, but passed for correctness/future use).
|
||||
(void)unwrap_code_op;
|
||||
ZirInstTag unwrap_code_op, uint32_t payload_token) {
|
||||
AstGenCtx* ag = gz->astgen;
|
||||
const Ast* tree = ag->tree;
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
@@ -6283,10 +6332,33 @@ static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
||||
if (do_err_trace && nodeMayAppendToErrorTrace(tree, nd.lhs))
|
||||
addSaveErrRetIndex(&else_scope, ZIR_REF_NONE);
|
||||
|
||||
// Error capture scope (AstGen.zig:6102-6123).
|
||||
ScopeLocalVal err_val_scope;
|
||||
memset(&err_val_scope, 0, sizeof(err_val_scope));
|
||||
Scope* else_sub_scope = &else_scope.base;
|
||||
if (payload_token != UINT32_MAX) {
|
||||
if (tokenIsUnderscore(tree, payload_token)) {
|
||||
// Discard |_| — else_sub_scope stays as &else_scope.base.
|
||||
} else {
|
||||
uint32_t err_name = identAsString(ag, payload_token);
|
||||
uint32_t err_inst
|
||||
= addUnNode(&else_scope, unwrap_code_op, operand, node);
|
||||
err_val_scope = (ScopeLocalVal) {
|
||||
.base = { .tag = SCOPE_LOCAL_VAL },
|
||||
.parent = &else_scope.base,
|
||||
.gen_zir = &else_scope,
|
||||
.inst = err_inst,
|
||||
.token_src = payload_token,
|
||||
.name = err_name,
|
||||
};
|
||||
else_sub_scope = &err_val_scope.base;
|
||||
}
|
||||
}
|
||||
|
||||
// Use fullBodyExpr (not expr) to inline unlabeled blocks
|
||||
// (AstGen.zig:6125).
|
||||
uint32_t else_result
|
||||
= fullBodyExpr(&else_scope, &else_scope.base, break_rl, nd.rhs);
|
||||
= fullBodyExpr(&else_scope, else_sub_scope, break_rl, nd.rhs);
|
||||
if (!endsWithNoReturn(&else_scope)) {
|
||||
// restoreErrRetIndex (AstGen.zig:6128-6129).
|
||||
if (do_err_trace)
|
||||
@@ -8234,10 +8306,10 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
|
||||
const uint32_t* statements, uint32_t stmt_count) {
|
||||
AstGenCtx* ag = gz->astgen;
|
||||
// Stack-allocated scope storage for local variables and defers.
|
||||
// Max 64 local variable declarations and 64 defers per block.
|
||||
ScopeLocalVal val_scopes[64];
|
||||
ScopeLocalPtr ptr_scopes[64];
|
||||
ScopeDefer defer_scopes[64];
|
||||
// Max 128 local variable declarations and 128 defers per block.
|
||||
ScopeLocalVal val_scopes[128];
|
||||
ScopeLocalPtr ptr_scopes[128];
|
||||
ScopeDefer defer_scopes[128];
|
||||
uint32_t val_idx = 0;
|
||||
uint32_t ptr_idx = 0;
|
||||
uint32_t defer_idx = 0;
|
||||
@@ -8314,7 +8386,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
|
||||
case AST_NODE_SIMPLE_VAR_DECL:
|
||||
case AST_NODE_LOCAL_VAR_DECL:
|
||||
case AST_NODE_ALIGNED_VAR_DECL:
|
||||
if (val_idx < 64 && ptr_idx < 64) {
|
||||
if (val_idx < 128 && ptr_idx < 128) {
|
||||
varDecl(gz, cur_scope, stmt, &val_scopes[val_idx],
|
||||
&ptr_scopes[ptr_idx], &cur_scope);
|
||||
// Check which one was used: if scope now points to
|
||||
@@ -8330,7 +8402,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
|
||||
// defer/errdefer (AstGen.zig:2580-2581).
|
||||
case AST_NODE_DEFER:
|
||||
case AST_NODE_ERRDEFER: {
|
||||
if (defer_idx >= 64) {
|
||||
if (defer_idx >= 128) {
|
||||
SET_ERROR(ag);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -793,6 +793,9 @@ test "astgen: corpus test_all.zig" {
|
||||
}
|
||||
|
||||
test "astgen: corpus build.zig" {
|
||||
// TODO: 6 extra instructions — missing switchExprErrUnion optimization
|
||||
// (catch |err| switch(err) pattern emits SWITCH_BLOCK instead of
|
||||
// SWITCH_BLOCK_ERR_UNION).
|
||||
if (true) return error.SkipZigTest;
|
||||
const gpa = std.testing.allocator;
|
||||
try corpusCheck(gpa, @embedFile("../build.zig"));
|
||||
|
||||
Reference in New Issue
Block a user