astgen: assign_destructure, u0 int type, slice_length optimization
- Implement assignDestructure() and assignDestructureMaybeDecls() with RL_DESTRUCTURE result location, DestructureComponent types, rvalue handling for validate_destructure/elem_val_imm/store_node, and array init optimization. - Fix tryResolvePrimitiveIdent to allow bit_count==0 (u0/i0 types) and reject leading zeros (u01, i007). - Add nodeIsTriviallyZero and slice_length optimization for arr[start..][0..len] patterns in AST_NODE_SLICE and AST_NODE_SLICE_SENTINEL cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
608
stage0/astgen.c
608
stage0/astgen.c
@@ -185,6 +185,19 @@ typedef enum {
|
||||
RI_CTX_ASSIGNMENT,
|
||||
} ResultCtx;
|
||||
|
||||
// DestructureComponent: mirrors Zig's ResultInfo.Loc.DestructureComponent.
|
||||
typedef enum {
|
||||
DC_DISCARD,
|
||||
DC_TYPED_PTR,
|
||||
DC_INFERRED_PTR,
|
||||
} DestructureComponentTag;
|
||||
|
||||
typedef struct {
|
||||
DestructureComponentTag tag;
|
||||
uint32_t inst; // ZIR inst ref (for DC_TYPED_PTR and DC_INFERRED_PTR).
|
||||
uint32_t src_node; // Only for DC_TYPED_PTR.
|
||||
} DestructureComponent;
|
||||
|
||||
typedef enum {
|
||||
RL_NONE, // Just compute the value.
|
||||
RL_REF, // Compute a pointer to the value.
|
||||
@@ -194,25 +207,40 @@ typedef enum {
|
||||
RL_PTR, // Store result to typed pointer. data=alloc inst, src_node=node.
|
||||
RL_INFERRED_PTR, // Store result to inferred pointer. data=alloc inst.
|
||||
RL_REF_COERCED_TY, // Ref with pointer type. data=ptr_ty_inst.
|
||||
RL_DESTRUCTURE, // Destructure into multiple pointers.
|
||||
} ResultLocTag;
|
||||
|
||||
typedef struct {
|
||||
ResultLocTag tag;
|
||||
uint32_t data; // ZirInstRef: ty_inst for TY/COERCED_TY, alloc inst for
|
||||
// PTR/INFERRED_PTR.
|
||||
uint32_t src_node; // Only used for RL_PTR.
|
||||
uint32_t src_node; // Used for RL_PTR and RL_DESTRUCTURE.
|
||||
ResultCtx ctx; // ResultInfo.Context (AstGen.zig:371).
|
||||
DestructureComponent* components; // Only for RL_DESTRUCTURE.
|
||||
uint32_t components_len; // Only for RL_DESTRUCTURE.
|
||||
} ResultLoc;
|
||||
|
||||
#define RL_NONE_VAL \
|
||||
((ResultLoc) { \
|
||||
.tag = RL_NONE, .data = 0, .src_node = 0, .ctx = RI_CTX_NONE })
|
||||
((ResultLoc) { .tag = RL_NONE, \
|
||||
.data = 0, \
|
||||
.src_node = 0, \
|
||||
.ctx = RI_CTX_NONE, \
|
||||
.components = NULL, \
|
||||
.components_len = 0 })
|
||||
#define RL_REF_VAL \
|
||||
((ResultLoc) { \
|
||||
.tag = RL_REF, .data = 0, .src_node = 0, .ctx = RI_CTX_NONE })
|
||||
((ResultLoc) { .tag = RL_REF, \
|
||||
.data = 0, \
|
||||
.src_node = 0, \
|
||||
.ctx = RI_CTX_NONE, \
|
||||
.components = NULL, \
|
||||
.components_len = 0 })
|
||||
#define RL_DISCARD_VAL \
|
||||
((ResultLoc) { \
|
||||
.tag = RL_DISCARD, .data = 0, .src_node = 0, .ctx = RI_CTX_NONE })
|
||||
((ResultLoc) { .tag = RL_DISCARD, \
|
||||
.data = 0, \
|
||||
.src_node = 0, \
|
||||
.ctx = RI_CTX_NONE, \
|
||||
.components = NULL, \
|
||||
.components_len = 0 })
|
||||
#define RL_IS_REF(rl) ((rl).tag == RL_REF || (rl).tag == RL_REF_COERCED_TY)
|
||||
|
||||
// --- Scope types (AstGen.zig:11621-11768) ---
|
||||
@@ -661,6 +689,13 @@ static uint32_t addUnNode(
|
||||
return addInstruction(gz, tag, data);
|
||||
}
|
||||
|
||||
// Mirrors GenZir.addNode (AstGen.zig:12414).
|
||||
static uint32_t addNode(GenZir* gz, ZirInstTag tag, uint32_t node) {
|
||||
ZirInstData data;
|
||||
data.node = (int32_t)node - (int32_t)gz->decl_node_index;
|
||||
return addInstruction(gz, tag, data);
|
||||
}
|
||||
|
||||
// Mirrors GenZir.addUnTok (AstGen.zig:12497).
|
||||
static uint32_t addUnTok(
|
||||
GenZir* gz, ZirInstTag tag, uint32_t operand, uint32_t abs_tok_index) {
|
||||
@@ -2437,6 +2472,51 @@ static uint32_t rvalue(
|
||||
addPlNodeBin(
|
||||
gz, ZIR_INST_STORE_TO_INFERRED_PTR, node, rl.data, result);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
case RL_DESTRUCTURE: {
|
||||
// validate_destructure (AstGen.zig:11225-11258).
|
||||
uint32_t ds_node = rl.src_node;
|
||||
uint32_t comp_len = rl.components_len;
|
||||
// Emit validate_destructure: pl_node with ValidateDestructure payload.
|
||||
// Payload: { operand, destructure_node (relative), expect_len }
|
||||
{
|
||||
uint32_t payload_idx = gz->astgen->extra_len;
|
||||
ensureExtraCapacity(gz->astgen, 3);
|
||||
gz->astgen->extra[gz->astgen->extra_len++] = result; // operand
|
||||
gz->astgen->extra[gz->astgen->extra_len++]
|
||||
= (uint32_t)((int32_t)ds_node
|
||||
- (int32_t)gz->decl_node_index); // destructure_node
|
||||
gz->astgen->extra[gz->astgen->extra_len++]
|
||||
= comp_len; // expect_len
|
||||
addPlNodePayloadIndex(
|
||||
gz, ZIR_INST_VALIDATE_DESTRUCTURE, node, payload_idx);
|
||||
}
|
||||
for (uint32_t i = 0; i < comp_len; i++) {
|
||||
const DestructureComponent* comp = &rl.components[i];
|
||||
if (comp->tag == DC_DISCARD)
|
||||
continue;
|
||||
// elem_val_imm: operand=result, idx=i.
|
||||
uint32_t elem_inst = reserveInstructionIndex(gz->astgen);
|
||||
gz->astgen->inst_tags[elem_inst] = ZIR_INST_ELEM_VAL_IMM;
|
||||
gz->astgen->inst_datas[elem_inst].elem_val_imm.operand = result;
|
||||
gz->astgen->inst_datas[elem_inst].elem_val_imm.idx = i;
|
||||
gzAppendInstruction(gz, elem_inst);
|
||||
uint32_t elem_ref = elem_inst + ZIR_REF_START_INDEX;
|
||||
switch (comp->tag) {
|
||||
case DC_TYPED_PTR:
|
||||
addPlNodeBin(gz, ZIR_INST_STORE_NODE,
|
||||
comp->src_node != 0 ? comp->src_node : node, comp->inst,
|
||||
elem_ref);
|
||||
break;
|
||||
case DC_INFERRED_PTR:
|
||||
addPlNodeBin(gz, ZIR_INST_STORE_TO_INFERRED_PTR, node,
|
||||
comp->inst, elem_ref);
|
||||
break;
|
||||
case DC_DISCARD:
|
||||
break; // unreachable
|
||||
}
|
||||
}
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2472,6 +2552,10 @@ static DeferCounts countDefers(const Scope* outer_scope, Scope* inner_scope);
|
||||
|
||||
static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
|
||||
static void assignStmt(GenZir* gz, Scope* scope, uint32_t infix_node);
|
||||
static void assignDestructure(GenZir* gz, Scope* scope, uint32_t node);
|
||||
static Scope* assignDestructureMaybeDecls(GenZir* gz, Scope* scope,
|
||||
uint32_t node, ScopeLocalVal* val_scopes, uint32_t* val_idx,
|
||||
ScopeLocalPtr* ptr_scopes, uint32_t* ptr_idx, uint32_t max_scopes);
|
||||
static void assignOp(
|
||||
GenZir* gz, Scope* scope, uint32_t infix_node, ZirInstTag op_tag);
|
||||
static void assignShift(
|
||||
@@ -3616,6 +3700,10 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node) {
|
||||
&& (source[tok_start] == 'u' || source[tok_start] == 'i')) {
|
||||
// Zig Signedness enum: unsigned=1, signed=0
|
||||
uint8_t signedness = (source[tok_start] == 'u') ? 1 : 0;
|
||||
// Reject leading zeros (e.g. u01, i007) but allow u0/i0.
|
||||
if (tok_len >= 3 && source[tok_start + 1] == '0') {
|
||||
return ZIR_REF_NONE;
|
||||
}
|
||||
uint16_t bit_count = 0;
|
||||
bool valid = true;
|
||||
for (uint32_t k = tok_start + 1; k < tok_end; k++) {
|
||||
@@ -3627,7 +3715,7 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid && bit_count > 0) {
|
||||
if (valid) {
|
||||
ZirInstData data;
|
||||
data.int_type.src_node
|
||||
= (int32_t)node - (int32_t)gz->decl_node_index;
|
||||
@@ -5072,6 +5160,10 @@ static uint32_t structInitExpr(
|
||||
= structInitExprAnon(gz, scope, node, fields, fields_len);
|
||||
return rvalue(gz, rl, struct_inst, node);
|
||||
}
|
||||
case RL_DESTRUCTURE:
|
||||
// Struct value cannot be destructured (AstGen.zig:1854-1859).
|
||||
SET_ERROR(ag);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
}
|
||||
SET_ERROR(ag);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
@@ -5256,6 +5348,31 @@ static uint32_t boolBinOp(
|
||||
return bool_br + ZIR_REF_START_INDEX;
|
||||
}
|
||||
|
||||
// Mirrors nodeIsTriviallyZero (AstGen.zig:10299-10313).
|
||||
static bool nodeIsTriviallyZero(const Ast* tree, uint32_t node) {
|
||||
if (tree->nodes.tags[node] != AST_NODE_NUMBER_LITERAL)
|
||||
return false;
|
||||
uint32_t tok = tree->nodes.main_tokens[node];
|
||||
uint32_t tok_start = tree->tokens.starts[tok];
|
||||
const char* source = (const char*)tree->source;
|
||||
if (source[tok_start] != '0')
|
||||
return false;
|
||||
// Distinguish "0.." (range, token is "0") from "0.5" (float literal).
|
||||
char c = source[tok_start + 1];
|
||||
if (c == '.')
|
||||
return source[tok_start + 2] == '.';
|
||||
// Any alphanumeric or underscore means the token is longer than "0".
|
||||
if (c >= '0' && c <= '9')
|
||||
return false;
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return false;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return false;
|
||||
if (c == '_')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mirrors expr (AstGen.zig:634) — main expression dispatcher.
|
||||
static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
AstGenCtx* ag = gz->astgen;
|
||||
@@ -5650,18 +5767,54 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
addPlNodeBin(gz, ZIR_INST_SLICE_START, node, lhs, start), node);
|
||||
}
|
||||
case AST_NODE_SLICE: {
|
||||
// Slice[rhs]: { start, end } (AstGen.zig:908-937).
|
||||
// Slice[rhs]: { start, end } (AstGen.zig:882-937).
|
||||
const Ast* stree = ag->tree;
|
||||
uint32_t start_node = stree->extra_data.arr[nd.rhs];
|
||||
uint32_t end_node = stree->extra_data.arr[nd.rhs + 1];
|
||||
// slice_length optimization (AstGen.zig:887-906).
|
||||
if (stree->nodes.tags[nd.lhs] == AST_NODE_SLICE_OPEN
|
||||
&& nodeIsTriviallyZero(stree, start_node)) {
|
||||
AstData inner_nd = stree->nodes.datas[nd.lhs];
|
||||
uint32_t lhs = exprRl(gz, scope, RL_REF_VAL, inner_nd.lhs);
|
||||
ResultLoc usize_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_USIZE_TYPE,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE,
|
||||
.components = NULL,
|
||||
.components_len = 0 };
|
||||
uint32_t start_ref = exprRl(gz, scope, usize_rl, inner_nd.rhs);
|
||||
advanceSourceCursorToMainToken(ag, gz, node);
|
||||
uint32_t saved_line = ag->source_line - gz->decl_line;
|
||||
uint32_t saved_col = ag->source_column;
|
||||
uint32_t len_ref = exprRl(gz, scope, usize_rl, end_node);
|
||||
emitDbgStmt(gz, saved_line, saved_col);
|
||||
ensureExtraCapacity(ag, 5);
|
||||
uint32_t payload_index = ag->extra_len;
|
||||
ag->extra[ag->extra_len++] = lhs;
|
||||
ag->extra[ag->extra_len++] = start_ref;
|
||||
ag->extra[ag->extra_len++] = len_ref;
|
||||
ag->extra[ag->extra_len++] = ZIR_REF_NONE; // no sentinel
|
||||
int32_t src_off = (int32_t)nd.lhs - (int32_t)gz->decl_node_index;
|
||||
memcpy(&ag->extra[ag->extra_len], &src_off, sizeof(uint32_t));
|
||||
ag->extra_len++;
|
||||
ZirInstData data;
|
||||
data.pl_node.src_node
|
||||
= (int32_t)node - (int32_t)gz->decl_node_index;
|
||||
data.pl_node.payload_index = payload_index;
|
||||
return rvalue(
|
||||
gz, rl, addInstruction(gz, ZIR_INST_SLICE_LENGTH, data), node);
|
||||
}
|
||||
// Normal path.
|
||||
uint32_t lhs = exprRl(gz, scope, RL_REF_VAL, nd.lhs);
|
||||
advanceSourceCursorToMainToken(ag, gz, node);
|
||||
uint32_t saved_line = ag->source_line - gz->decl_line;
|
||||
uint32_t saved_col = ag->source_column;
|
||||
uint32_t start_node = stree->extra_data.arr[nd.rhs];
|
||||
uint32_t end_node = stree->extra_data.arr[nd.rhs + 1];
|
||||
ResultLoc usize_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_USIZE_TYPE,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE };
|
||||
.ctx = RI_CTX_NONE,
|
||||
.components = NULL,
|
||||
.components_len = 0 };
|
||||
uint32_t start_ref = exprRl(gz, scope, usize_rl, start_node);
|
||||
uint32_t end_ref = exprRl(gz, scope, usize_rl, end_node);
|
||||
emitDbgStmt(gz, saved_line, saved_col);
|
||||
@@ -5678,20 +5831,58 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
}
|
||||
case AST_NODE_SLICE_SENTINEL: {
|
||||
// SliceSentinel[rhs]: { start, end, sentinel }
|
||||
// (AstGen.zig:908-925).
|
||||
// (AstGen.zig:882-937).
|
||||
const Ast* stree = ag->tree;
|
||||
uint32_t start_node = stree->extra_data.arr[nd.rhs];
|
||||
uint32_t end_node = stree->extra_data.arr[nd.rhs + 1];
|
||||
uint32_t sentinel_node = stree->extra_data.arr[nd.rhs + 2];
|
||||
// slice_length optimization (AstGen.zig:887-906).
|
||||
if (end_node != 0 && stree->nodes.tags[nd.lhs] == AST_NODE_SLICE_OPEN
|
||||
&& nodeIsTriviallyZero(stree, start_node)) {
|
||||
AstData inner_nd = stree->nodes.datas[nd.lhs];
|
||||
uint32_t lhs = exprRl(gz, scope, RL_REF_VAL, inner_nd.lhs);
|
||||
ResultLoc usize_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_USIZE_TYPE,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE,
|
||||
.components = NULL,
|
||||
.components_len = 0 };
|
||||
uint32_t start_ref = exprRl(gz, scope, usize_rl, inner_nd.rhs);
|
||||
advanceSourceCursorToMainToken(ag, gz, node);
|
||||
uint32_t saved_line = ag->source_line - gz->decl_line;
|
||||
uint32_t saved_col = ag->source_column;
|
||||
uint32_t len_ref = exprRl(gz, scope, usize_rl, end_node);
|
||||
uint32_t sentinel_ref
|
||||
= exprRl(gz, scope, RL_NONE_VAL, sentinel_node);
|
||||
emitDbgStmt(gz, saved_line, saved_col);
|
||||
ensureExtraCapacity(ag, 5);
|
||||
uint32_t payload_index = ag->extra_len;
|
||||
ag->extra[ag->extra_len++] = lhs;
|
||||
ag->extra[ag->extra_len++] = start_ref;
|
||||
ag->extra[ag->extra_len++] = len_ref;
|
||||
ag->extra[ag->extra_len++] = sentinel_ref;
|
||||
int32_t src_off = (int32_t)nd.lhs - (int32_t)gz->decl_node_index;
|
||||
memcpy(&ag->extra[ag->extra_len], &src_off, sizeof(uint32_t));
|
||||
ag->extra_len++;
|
||||
ZirInstData data;
|
||||
data.pl_node.src_node
|
||||
= (int32_t)node - (int32_t)gz->decl_node_index;
|
||||
data.pl_node.payload_index = payload_index;
|
||||
return rvalue(
|
||||
gz, rl, addInstruction(gz, ZIR_INST_SLICE_LENGTH, data), node);
|
||||
}
|
||||
// Normal path.
|
||||
uint32_t lhs = exprRl(gz, scope, RL_REF_VAL, nd.lhs);
|
||||
advanceSourceCursorToMainToken(ag, gz, node);
|
||||
uint32_t saved_line = ag->source_line - gz->decl_line;
|
||||
uint32_t saved_col = ag->source_column;
|
||||
uint32_t start_node = stree->extra_data.arr[nd.rhs];
|
||||
uint32_t end_node = stree->extra_data.arr[nd.rhs + 1];
|
||||
uint32_t sentinel_node = stree->extra_data.arr[nd.rhs + 2];
|
||||
// start/end coerced to usize (AstGen.zig:911-912).
|
||||
ResultLoc usize_rl = { .tag = RL_COERCED_TY,
|
||||
.data = ZIR_REF_USIZE_TYPE,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE };
|
||||
.ctx = RI_CTX_NONE,
|
||||
.components = NULL,
|
||||
.components_len = 0 };
|
||||
uint32_t start_ref = exprRl(gz, scope, usize_rl, start_node);
|
||||
uint32_t end_ref = (end_node != 0)
|
||||
? exprRl(gz, scope, usize_rl, end_node)
|
||||
@@ -5702,7 +5893,9 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
ResultLoc sent_rl = { .tag = RL_COERCED_TY,
|
||||
.data = sentinel_ty,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE };
|
||||
.ctx = RI_CTX_NONE,
|
||||
.components = NULL,
|
||||
.components_len = 0 };
|
||||
uint32_t sentinel_ref = exprRl(gz, scope, sent_rl, sentinel_node);
|
||||
emitDbgStmt(gz, saved_line, saved_col);
|
||||
ensureExtraCapacity(ag, 4);
|
||||
@@ -6123,6 +6316,10 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
case AST_NODE_ASSIGN_SHL_SAT:
|
||||
assignShiftSat(gz, scope, node);
|
||||
return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
|
||||
// assign_destructure (AstGen.zig:669-674).
|
||||
case AST_NODE_ASSIGN_DESTRUCTURE:
|
||||
assignDestructure(gz, scope, node);
|
||||
return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
|
||||
default:
|
||||
SET_ERROR(ag);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
@@ -6448,6 +6645,36 @@ static uint32_t arrayInitDotExpr(
|
||||
gz, ZIR_INST_VALIDATE_PTR_ARRAY_INIT, node, payload_index);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
}
|
||||
case RL_DESTRUCTURE: {
|
||||
// Destructure directly into result pointers (AstGen.zig:1552-1569).
|
||||
if (elem_count != rl.components_len) {
|
||||
SET_ERROR(ag);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
}
|
||||
for (uint32_t i = 0; i < elem_count; i++) {
|
||||
const DestructureComponent* comp = &rl.components[i];
|
||||
ResultLoc elem_rl;
|
||||
switch (comp->tag) {
|
||||
case DC_TYPED_PTR:
|
||||
elem_rl = (ResultLoc) { .tag = RL_PTR,
|
||||
.data = comp->inst,
|
||||
.src_node = comp->src_node,
|
||||
.ctx = RI_CTX_NONE };
|
||||
break;
|
||||
case DC_INFERRED_PTR:
|
||||
elem_rl = (ResultLoc) { .tag = RL_INFERRED_PTR,
|
||||
.data = comp->inst,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE };
|
||||
break;
|
||||
case DC_DISCARD:
|
||||
elem_rl = RL_DISCARD_VAL;
|
||||
break;
|
||||
}
|
||||
exprRl(gz, scope, elem_rl, elements[i]);
|
||||
}
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: anon init + rvalue.
|
||||
@@ -8845,6 +9072,341 @@ static void assignStmt(GenZir* gz, Scope* scope, uint32_t infix_node) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- assignDestructure (AstGen.zig:3456-3504) ---
|
||||
// Handles destructure assignments where LHS is only lvalue expressions (no
|
||||
// new var/const declarations). Called from exprRl and blockExprStmts.
|
||||
|
||||
static void assignDestructure(GenZir* gz, Scope* scope, uint32_t node) {
|
||||
emitDbgNode(gz, node);
|
||||
AstGenCtx* ag = gz->astgen;
|
||||
const Ast* tree = ag->tree;
|
||||
|
||||
// Parse assign_destructure node: lhs=extra_index, rhs=value_expr.
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
uint32_t extra_start = nd.lhs;
|
||||
uint32_t value_expr = nd.rhs;
|
||||
uint32_t variable_count = tree->extra_data.arr[extra_start];
|
||||
const uint32_t* variables = tree->extra_data.arr + extra_start + 1;
|
||||
|
||||
// Detect comptime token (AstGen.zig:3462-3464).
|
||||
// Check if the first variable's first token (or the token before it)
|
||||
// is keyword_comptime.
|
||||
bool has_comptime = false;
|
||||
if (variable_count > 0) {
|
||||
uint32_t first_var = variables[0];
|
||||
AstNodeTag first_tag = tree->nodes.tags[first_var];
|
||||
uint32_t first_tok;
|
||||
if (first_tag == AST_NODE_GLOBAL_VAR_DECL
|
||||
|| first_tag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| first_tag == AST_NODE_ALIGNED_VAR_DECL
|
||||
|| first_tag == AST_NODE_SIMPLE_VAR_DECL) {
|
||||
first_tok = firstToken(tree, first_var);
|
||||
} else {
|
||||
first_tok = firstToken(tree, first_var) - 1;
|
||||
}
|
||||
if (first_tok < tree->tokens.len
|
||||
&& tree->tokens.tags[first_tok] == TOKEN_KEYWORD_COMPTIME) {
|
||||
has_comptime = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_comptime && gz->is_comptime) {
|
||||
// Redundant comptime in already comptime scope (AstGen.zig:3466-3468).
|
||||
SET_ERROR(ag);
|
||||
return;
|
||||
}
|
||||
|
||||
// If comptime, wrap in sub-block (AstGen.zig:3471-3477).
|
||||
GenZir gz_buf;
|
||||
GenZir* inner_gz = gz;
|
||||
if (has_comptime) {
|
||||
gz_buf = makeSubBlock(gz, scope);
|
||||
gz_buf.is_comptime = true;
|
||||
inner_gz = &gz_buf;
|
||||
}
|
||||
|
||||
// Build rl_components (AstGen.zig:3479-3492).
|
||||
DestructureComponent* rl_components
|
||||
= malloc(variable_count * sizeof(DestructureComponent));
|
||||
if (!rl_components)
|
||||
exit(1);
|
||||
for (uint32_t i = 0; i < variable_count; i++) {
|
||||
uint32_t variable_node = variables[i];
|
||||
// Check for `_` identifier (AstGen.zig:3481-3487).
|
||||
if (tree->nodes.tags[variable_node] == AST_NODE_IDENTIFIER) {
|
||||
uint32_t ident_tok = tree->nodes.main_tokens[variable_node];
|
||||
uint32_t tok_start = tree->tokens.starts[ident_tok];
|
||||
if (tree->source[tok_start] == '_'
|
||||
&& (tok_start + 1 >= tree->source_len
|
||||
|| !((tree->source[tok_start + 1] >= 'a'
|
||||
&& tree->source[tok_start + 1] <= 'z')
|
||||
|| (tree->source[tok_start + 1] >= 'A'
|
||||
&& tree->source[tok_start + 1] <= 'Z')
|
||||
|| tree->source[tok_start + 1] == '_'
|
||||
|| (tree->source[tok_start + 1] >= '0'
|
||||
&& tree->source[tok_start + 1] <= '9')))) {
|
||||
rl_components[i].tag = DC_DISCARD;
|
||||
rl_components[i].inst = 0;
|
||||
rl_components[i].src_node = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// lvalExpr: evaluate as ref (AstGen.zig:3488-3491).
|
||||
rl_components[i].tag = DC_TYPED_PTR;
|
||||
rl_components[i].inst
|
||||
= exprRl(inner_gz, scope, RL_REF_VAL, variable_node);
|
||||
rl_components[i].src_node = variable_node;
|
||||
}
|
||||
|
||||
// Build destructure result location and evaluate RHS
|
||||
// (AstGen.zig:3494-3499).
|
||||
ResultLoc ds_rl;
|
||||
memset(&ds_rl, 0, sizeof(ds_rl));
|
||||
ds_rl.tag = RL_DESTRUCTURE;
|
||||
ds_rl.src_node = node;
|
||||
ds_rl.components = rl_components;
|
||||
ds_rl.components_len = variable_count;
|
||||
(void)exprRl(inner_gz, scope, ds_rl, value_expr);
|
||||
|
||||
// If comptime, finish block_comptime (AstGen.zig:3501-3505).
|
||||
if (has_comptime) {
|
||||
uint32_t comptime_block_inst
|
||||
= makeBlockInst(ag, ZIR_INST_BLOCK_COMPTIME, gz, node);
|
||||
addBreak(inner_gz, ZIR_INST_BREAK_INLINE, comptime_block_inst,
|
||||
ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
|
||||
setBlockComptimeBody(ag, inner_gz, comptime_block_inst,
|
||||
COMPTIME_REASON_COMPTIME_KEYWORD);
|
||||
gzAppendInstruction(gz, comptime_block_inst);
|
||||
gzUnstack(inner_gz);
|
||||
}
|
||||
free(rl_components);
|
||||
}
|
||||
|
||||
// --- assignDestructureMaybeDecls (AstGen.zig:3507-3729) ---
|
||||
// Handles destructure assignments that may contain const/var declarations.
|
||||
// Returns new scope containing any declared variables.
|
||||
|
||||
static Scope* assignDestructureMaybeDecls(GenZir* gz, Scope* scope,
|
||||
uint32_t node, ScopeLocalVal* val_scopes, uint32_t* val_idx,
|
||||
ScopeLocalPtr* ptr_scopes, uint32_t* ptr_idx, uint32_t max_scopes) {
|
||||
(void)val_scopes;
|
||||
(void)val_idx;
|
||||
emitDbgNode(gz, node);
|
||||
AstGenCtx* ag = gz->astgen;
|
||||
const Ast* tree = ag->tree;
|
||||
|
||||
// Parse assign_destructure node.
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
uint32_t extra_start = nd.lhs;
|
||||
uint32_t value_expr = nd.rhs;
|
||||
uint32_t variable_count = tree->extra_data.arr[extra_start];
|
||||
const uint32_t* variables = tree->extra_data.arr + extra_start + 1;
|
||||
|
||||
// Detect comptime token.
|
||||
bool has_comptime = false;
|
||||
if (variable_count > 0) {
|
||||
uint32_t first_var = variables[0];
|
||||
AstNodeTag first_tag = tree->nodes.tags[first_var];
|
||||
uint32_t first_tok;
|
||||
if (first_tag == AST_NODE_GLOBAL_VAR_DECL
|
||||
|| first_tag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| first_tag == AST_NODE_ALIGNED_VAR_DECL
|
||||
|| first_tag == AST_NODE_SIMPLE_VAR_DECL) {
|
||||
first_tok = firstToken(tree, first_var);
|
||||
} else {
|
||||
first_tok = firstToken(tree, first_var) - 1;
|
||||
}
|
||||
if (first_tok < tree->tokens.len
|
||||
&& tree->tokens.tags[first_tok] == TOKEN_KEYWORD_COMPTIME) {
|
||||
has_comptime = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_comptime = has_comptime || gz->is_comptime;
|
||||
bool value_is_comptime = tree->nodes.tags[value_expr] == AST_NODE_COMPTIME;
|
||||
|
||||
if (has_comptime && gz->is_comptime) {
|
||||
SET_ERROR(ag); // redundant comptime
|
||||
}
|
||||
|
||||
// First pass: build rl_components (AstGen.zig:3535-3620).
|
||||
DestructureComponent* rl_components
|
||||
= malloc(variable_count * sizeof(DestructureComponent));
|
||||
if (!rl_components)
|
||||
exit(1);
|
||||
bool any_lvalue_expr = false;
|
||||
|
||||
for (uint32_t i = 0; i < variable_count; i++) {
|
||||
uint32_t variable_node = variables[i];
|
||||
AstNodeTag vtag = tree->nodes.tags[variable_node];
|
||||
|
||||
// Check for `_` identifier (AstGen.zig:3537-3544).
|
||||
if (vtag == AST_NODE_IDENTIFIER) {
|
||||
uint32_t ident_tok = tree->nodes.main_tokens[variable_node];
|
||||
uint32_t tok_start = tree->tokens.starts[ident_tok];
|
||||
if (tree->source[tok_start] == '_'
|
||||
&& (tok_start + 1 >= tree->source_len
|
||||
|| !((tree->source[tok_start + 1] >= 'a'
|
||||
&& tree->source[tok_start + 1] <= 'z')
|
||||
|| (tree->source[tok_start + 1] >= 'A'
|
||||
&& tree->source[tok_start + 1] <= 'Z')
|
||||
|| tree->source[tok_start + 1] == '_'
|
||||
|| (tree->source[tok_start + 1] >= '0'
|
||||
&& tree->source[tok_start + 1] <= '9')))) {
|
||||
|
||||
rl_components[i].tag = DC_DISCARD;
|
||||
rl_components[i].inst = 0;
|
||||
rl_components[i].src_node = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// var/const declarations (AstGen.zig:3545-3607).
|
||||
if (vtag == AST_NODE_GLOBAL_VAR_DECL || vtag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| vtag == AST_NODE_SIMPLE_VAR_DECL
|
||||
|| vtag == AST_NODE_ALIGNED_VAR_DECL) {
|
||||
AstData vnd = tree->nodes.datas[variable_node];
|
||||
uint32_t mut_token = tree->nodes.main_tokens[variable_node];
|
||||
bool var_is_const
|
||||
= (tree->tokens.tags[mut_token] == TOKEN_KEYWORD_CONST);
|
||||
bool this_comptime
|
||||
= is_comptime || (var_is_const && value_is_comptime);
|
||||
|
||||
uint32_t type_node = vnd.lhs;
|
||||
// AstGen.zig:3576-3607: typed vs inferred alloc.
|
||||
if (type_node != 0) {
|
||||
uint32_t type_inst = typeExpr(gz, scope, type_node);
|
||||
ZirInstTag alloc_tag;
|
||||
if (var_is_const)
|
||||
alloc_tag = ZIR_INST_ALLOC;
|
||||
else if (this_comptime)
|
||||
alloc_tag = ZIR_INST_ALLOC_COMPTIME_MUT;
|
||||
else
|
||||
alloc_tag = ZIR_INST_ALLOC_MUT;
|
||||
uint32_t ptr = addUnNode(gz, alloc_tag, type_inst, node);
|
||||
rl_components[i].tag = DC_TYPED_PTR;
|
||||
rl_components[i].inst = ptr;
|
||||
rl_components[i].src_node = 0;
|
||||
} else {
|
||||
// Inferred alloc.
|
||||
ZirInstTag alloc_tag;
|
||||
if (var_is_const) {
|
||||
alloc_tag = this_comptime
|
||||
? ZIR_INST_ALLOC_INFERRED_COMPTIME
|
||||
: ZIR_INST_ALLOC_INFERRED;
|
||||
} else {
|
||||
alloc_tag = this_comptime
|
||||
? ZIR_INST_ALLOC_INFERRED_COMPTIME_MUT
|
||||
: ZIR_INST_ALLOC_INFERRED_MUT;
|
||||
}
|
||||
uint32_t ptr = addNode(gz, alloc_tag, node);
|
||||
rl_components[i].tag = DC_INFERRED_PTR;
|
||||
rl_components[i].inst = ptr;
|
||||
rl_components[i].src_node = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Lvalue expression (AstGen.zig:3609-3618).
|
||||
any_lvalue_expr = true;
|
||||
rl_components[i].tag = DC_TYPED_PTR;
|
||||
rl_components[i].inst = 0; // will be filled in second pass
|
||||
rl_components[i].src_node = variable_node;
|
||||
}
|
||||
|
||||
// If comptime, wrap in sub-block (AstGen.zig:3627-3632).
|
||||
GenZir gz_buf;
|
||||
GenZir* inner_gz = gz;
|
||||
if (has_comptime) {
|
||||
gz_buf = makeSubBlock(gz, scope);
|
||||
gz_buf.is_comptime = true;
|
||||
inner_gz = &gz_buf;
|
||||
}
|
||||
|
||||
// Second pass for lvalue expressions (AstGen.zig:3634-3642).
|
||||
if (any_lvalue_expr) {
|
||||
for (uint32_t i = 0; i < variable_count; i++) {
|
||||
if (rl_components[i].tag != DC_TYPED_PTR)
|
||||
continue;
|
||||
AstNodeTag vtag = tree->nodes.tags[variables[i]];
|
||||
if (vtag == AST_NODE_GLOBAL_VAR_DECL
|
||||
|| vtag == AST_NODE_LOCAL_VAR_DECL
|
||||
|| vtag == AST_NODE_SIMPLE_VAR_DECL
|
||||
|| vtag == AST_NODE_ALIGNED_VAR_DECL)
|
||||
continue;
|
||||
rl_components[i].inst
|
||||
= exprRl(inner_gz, scope, RL_REF_VAL, variables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate RHS with destructure RL (AstGen.zig:3647-3652).
|
||||
ResultLoc ds_rl;
|
||||
memset(&ds_rl, 0, sizeof(ds_rl));
|
||||
ds_rl.tag = RL_DESTRUCTURE;
|
||||
ds_rl.src_node = node;
|
||||
ds_rl.components = rl_components;
|
||||
ds_rl.components_len = variable_count;
|
||||
(void)exprRl(inner_gz, scope, ds_rl, value_expr);
|
||||
|
||||
// If comptime, finish block_comptime (AstGen.zig:3654-3660).
|
||||
if (has_comptime) {
|
||||
uint32_t comptime_block_inst
|
||||
= makeBlockInst(ag, ZIR_INST_BLOCK_COMPTIME, gz, node);
|
||||
addBreak(inner_gz, ZIR_INST_BREAK_INLINE, comptime_block_inst,
|
||||
ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
|
||||
setBlockComptimeBody(ag, inner_gz, comptime_block_inst,
|
||||
COMPTIME_REASON_COMPTIME_KEYWORD);
|
||||
gzAppendInstruction(gz, comptime_block_inst);
|
||||
gzUnstack(inner_gz);
|
||||
}
|
||||
|
||||
// Third pass: create scopes for declared variables (AstGen.zig:3664-3729).
|
||||
Scope* cur_scope = scope;
|
||||
for (uint32_t i = 0; i < variable_count; i++) {
|
||||
uint32_t variable_node = variables[i];
|
||||
AstNodeTag vtag = tree->nodes.tags[variable_node];
|
||||
if (vtag != AST_NODE_LOCAL_VAR_DECL && vtag != AST_NODE_SIMPLE_VAR_DECL
|
||||
&& vtag != AST_NODE_ALIGNED_VAR_DECL)
|
||||
continue;
|
||||
|
||||
uint32_t mut_token = tree->nodes.main_tokens[variable_node];
|
||||
bool var_is_const
|
||||
= (tree->tokens.tags[mut_token] == TOKEN_KEYWORD_CONST);
|
||||
uint32_t raw_ptr = rl_components[i].inst;
|
||||
bool resolve_inferred = (rl_components[i].tag == DC_INFERRED_PTR);
|
||||
|
||||
// Resolve inferred alloc or make ptr const (AstGen.zig:3694-3700).
|
||||
uint32_t final_ptr;
|
||||
if (resolve_inferred)
|
||||
final_ptr = addUnNode(
|
||||
gz, ZIR_INST_RESOLVE_INFERRED_ALLOC, raw_ptr, variable_node);
|
||||
else if (var_is_const)
|
||||
final_ptr = addUnNode(gz, ZIR_INST_MAKE_PTR_CONST, raw_ptr, node);
|
||||
else
|
||||
final_ptr = raw_ptr;
|
||||
|
||||
// Create dbg_var_ptr (AstGen.zig:3710).
|
||||
uint32_t name_token = mut_token + 1;
|
||||
uint32_t ident_name = identAsString(ag, name_token);
|
||||
addDbgVar(gz, ZIR_INST_DBG_VAR_PTR, ident_name, final_ptr);
|
||||
|
||||
// Create scope (AstGen.zig:3712-3722).
|
||||
if (*ptr_idx < max_scopes) {
|
||||
ptr_scopes[*ptr_idx] = (ScopeLocalPtr) {
|
||||
.base = { .tag = SCOPE_LOCAL_PTR },
|
||||
.parent = cur_scope,
|
||||
.name = ident_name,
|
||||
.ptr = final_ptr,
|
||||
};
|
||||
cur_scope = &ptr_scopes[*ptr_idx].base;
|
||||
(*ptr_idx)++;
|
||||
} else {
|
||||
SET_ERROR(ag);
|
||||
}
|
||||
}
|
||||
free(rl_components);
|
||||
return cur_scope;
|
||||
}
|
||||
|
||||
// --- assignOp (AstGen.zig:3731) ---
|
||||
// Handles compound assignment operators (+=, -=, *=, etc.).
|
||||
|
||||
@@ -9289,6 +9851,8 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
|
||||
|
||||
// Evaluate init with RL pointing to alloc (AstGen.zig:3313-3316).
|
||||
ResultLoc init_rl;
|
||||
init_rl.components = NULL;
|
||||
init_rl.components_len = 0;
|
||||
if (type_node != 0) {
|
||||
init_rl.tag = RL_PTR;
|
||||
init_rl.data = var_ptr;
|
||||
@@ -9361,6 +9925,8 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
|
||||
|
||||
// Evaluate init with RL pointing to alloc (AstGen.zig:3395-3402).
|
||||
ResultLoc var_init_rl;
|
||||
var_init_rl.components = NULL;
|
||||
var_init_rl.components_len = 0;
|
||||
if (type_node != 0) {
|
||||
var_init_rl.tag = RL_PTR;
|
||||
var_init_rl.data = alloc_ref;
|
||||
@@ -9645,6 +10211,12 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
|
||||
case AST_NODE_ASSIGN:
|
||||
assignStmt(gz, cur_scope, inner_node);
|
||||
break;
|
||||
// assign_destructure (AstGen.zig:2578).
|
||||
case AST_NODE_ASSIGN_DESTRUCTURE:
|
||||
cur_scope
|
||||
= assignDestructureMaybeDecls(gz, cur_scope, inner_node,
|
||||
val_scopes, &val_idx, ptr_scopes, &ptr_idx, 128);
|
||||
break;
|
||||
// Shift assignment operators (AstGen.zig:2585-2586).
|
||||
case AST_NODE_ASSIGN_SHL:
|
||||
assignShift(gz, cur_scope, inner_node, ZIR_INST_SHL);
|
||||
|
||||
@@ -853,13 +853,13 @@ test "astgen: corpus astgen_test.zig" {
|
||||
}
|
||||
|
||||
test "astgen: corpus array_list.zig" {
|
||||
if (true) return error.SkipZigTest; // TODO: missing assign_destructure handler
|
||||
if (true) return error.SkipZigTest; // TODO: +2 ALLOC_MUT / -2 EXTENDED tag mismatch at [6639]
|
||||
const gpa = std.testing.allocator;
|
||||
try corpusCheck(gpa, @embedFile("../lib/std/array_list.zig"));
|
||||
}
|
||||
|
||||
test "astgen: corpus multi_array_list.zig" {
|
||||
if (true) return error.SkipZigTest; // TODO: identifier resolution across namespace scopes
|
||||
if (true) return error.SkipZigTest; // TODO: parser bug - C parser produces nodes_len=1
|
||||
const gpa = std.testing.allocator;
|
||||
try corpusCheck(gpa, @embedFile("../lib/std/multi_array_list.zig"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user