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);
|
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_statement);
|
||||||
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
||||||
uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op,
|
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(
|
static uint32_t arrayInitDotExpr(
|
||||||
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
||||||
static uint32_t switchExpr(
|
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_ARRAY_LENGTH 33
|
||||||
#define COMPTIME_REASON_ALIGN 50
|
#define COMPTIME_REASON_ALIGN 50
|
||||||
#define COMPTIME_REASON_ADDRSPACE 51
|
#define COMPTIME_REASON_ADDRSPACE 51
|
||||||
|
#define COMPTIME_REASON_FIELD_NAME 42
|
||||||
#define COMPTIME_REASON_COMPTIME_KEYWORD 53
|
#define COMPTIME_REASON_COMPTIME_KEYWORD 53
|
||||||
#define COMPTIME_REASON_SWITCH_ITEM 56
|
#define COMPTIME_REASON_SWITCH_ITEM 56
|
||||||
#define COMPTIME_REASON_TUPLE_FIELD_DEFAULT_VALUE 57
|
#define COMPTIME_REASON_TUPLE_FIELD_DEFAULT_VALUE 57
|
||||||
@@ -2923,6 +2924,51 @@ static uint32_t builtinCall(
|
|||||||
return rvalue(gz, rl,
|
return rvalue(gz, rl,
|
||||||
addPlNodeBin(gz, ZIR_INST_MAX, node, a, b), node);
|
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
|
// clang-format on
|
||||||
|
|
||||||
// TODO: handle other builtins.
|
// 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)) {
|
if (RL_IS_REF(rl)) {
|
||||||
return orelseCatchExpr(gz, scope, rl, node,
|
return orelseCatchExpr(gz, scope, rl, node,
|
||||||
ZIR_INST_IS_NON_NULL_PTR, ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE_PTR,
|
ZIR_INST_IS_NON_NULL_PTR, ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE_PTR,
|
||||||
(ZirInstTag)0);
|
(ZirInstTag)0, UINT32_MAX);
|
||||||
} else {
|
} else {
|
||||||
return orelseCatchExpr(gz, scope, rl, node, ZIR_INST_IS_NON_NULL,
|
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).
|
// 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)) {
|
if (RL_IS_REF(rl)) {
|
||||||
return orelseCatchExpr(gz, scope, rl, node,
|
return orelseCatchExpr(gz, scope, rl, node,
|
||||||
ZIR_INST_IS_NON_ERR_PTR, ZIR_INST_ERR_UNION_PAYLOAD_UNSAFE_PTR,
|
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 {
|
} else {
|
||||||
return orelseCatchExpr(gz, scope, rl, node, ZIR_INST_IS_NON_ERR,
|
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).
|
// Block expressions (AstGen.zig:984-992).
|
||||||
case AST_NODE_BLOCK_TWO:
|
case AST_NODE_BLOCK_TWO:
|
||||||
case AST_NODE_BLOCK_TWO_SEMICOLON:
|
case AST_NODE_BLOCK_TWO_SEMICOLON:
|
||||||
@@ -6220,10 +6272,7 @@ static uint32_t forExpr(
|
|||||||
|
|
||||||
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
|
||||||
uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op,
|
uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op,
|
||||||
ZirInstTag unwrap_code_op) {
|
ZirInstTag unwrap_code_op, uint32_t payload_token) {
|
||||||
// unwrap_code_op used for catch payload capture scope (not yet
|
|
||||||
// implemented in C, but passed for correctness/future use).
|
|
||||||
(void)unwrap_code_op;
|
|
||||||
AstGenCtx* ag = gz->astgen;
|
AstGenCtx* ag = gz->astgen;
|
||||||
const Ast* tree = ag->tree;
|
const Ast* tree = ag->tree;
|
||||||
AstData nd = tree->nodes.datas[node];
|
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))
|
if (do_err_trace && nodeMayAppendToErrorTrace(tree, nd.lhs))
|
||||||
addSaveErrRetIndex(&else_scope, ZIR_REF_NONE);
|
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
|
// Use fullBodyExpr (not expr) to inline unlabeled blocks
|
||||||
// (AstGen.zig:6125).
|
// (AstGen.zig:6125).
|
||||||
uint32_t else_result
|
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)) {
|
if (!endsWithNoReturn(&else_scope)) {
|
||||||
// restoreErrRetIndex (AstGen.zig:6128-6129).
|
// restoreErrRetIndex (AstGen.zig:6128-6129).
|
||||||
if (do_err_trace)
|
if (do_err_trace)
|
||||||
@@ -8234,10 +8306,10 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
|
|||||||
const uint32_t* statements, uint32_t stmt_count) {
|
const uint32_t* statements, uint32_t stmt_count) {
|
||||||
AstGenCtx* ag = gz->astgen;
|
AstGenCtx* ag = gz->astgen;
|
||||||
// Stack-allocated scope storage for local variables and defers.
|
// Stack-allocated scope storage for local variables and defers.
|
||||||
// Max 64 local variable declarations and 64 defers per block.
|
// Max 128 local variable declarations and 128 defers per block.
|
||||||
ScopeLocalVal val_scopes[64];
|
ScopeLocalVal val_scopes[128];
|
||||||
ScopeLocalPtr ptr_scopes[64];
|
ScopeLocalPtr ptr_scopes[128];
|
||||||
ScopeDefer defer_scopes[64];
|
ScopeDefer defer_scopes[128];
|
||||||
uint32_t val_idx = 0;
|
uint32_t val_idx = 0;
|
||||||
uint32_t ptr_idx = 0;
|
uint32_t ptr_idx = 0;
|
||||||
uint32_t defer_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_SIMPLE_VAR_DECL:
|
||||||
case AST_NODE_LOCAL_VAR_DECL:
|
case AST_NODE_LOCAL_VAR_DECL:
|
||||||
case AST_NODE_ALIGNED_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],
|
varDecl(gz, cur_scope, stmt, &val_scopes[val_idx],
|
||||||
&ptr_scopes[ptr_idx], &cur_scope);
|
&ptr_scopes[ptr_idx], &cur_scope);
|
||||||
// Check which one was used: if scope now points to
|
// 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).
|
// defer/errdefer (AstGen.zig:2580-2581).
|
||||||
case AST_NODE_DEFER:
|
case AST_NODE_DEFER:
|
||||||
case AST_NODE_ERRDEFER: {
|
case AST_NODE_ERRDEFER: {
|
||||||
if (defer_idx >= 64) {
|
if (defer_idx >= 128) {
|
||||||
SET_ERROR(ag);
|
SET_ERROR(ag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -793,6 +793,9 @@ test "astgen: corpus test_all.zig" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "astgen: corpus build.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;
|
if (true) return error.SkipZigTest;
|
||||||
const gpa = std.testing.allocator;
|
const gpa = std.testing.allocator;
|
||||||
try corpusCheck(gpa, @embedFile("../build.zig"));
|
try corpusCheck(gpa, @embedFile("../build.zig"));
|
||||||
|
|||||||
Reference in New Issue
Block a user