commit 9b0020fe40074d122fa93f2987d0ad20fcc23c42 (tree)
parent f809b08c8b243c7196b652994214e0d8f9a33bd1
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Sun, 15 Feb 2026 18:23:33 +0000
astgen: port inline assembly support and fix extern fn handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
2 files changed, 664 insertions(+), 429 deletions(-)
diff --git a/stage0/astgen.c b/stage0/astgen.c
@@ -2785,6 +2785,8 @@ static void addRestoreErrRetIndexBlock(
static void restoreErrRetIndex(GenZir* gz, uint32_t block_inst, ResultLoc rl,
uint32_t node, uint32_t result);
static uint32_t identAsString(AstGenCtx* ag, uint32_t token);
+static uint32_t localVarRef(GenZir* gz, Scope* scope, ResultLoc rl,
+ uint32_t node, uint32_t ident_token, uint32_t name_str);
static uint32_t lastToken(const Ast* tree, uint32_t node);
static uint32_t simpleBinOp(
GenZir* gz, Scope* scope, uint32_t node, ZirInstTag tag);
@@ -2935,6 +2937,8 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
#define COMPTIME_REASON_CALL_MODIFIER 18
#define COMPTIME_REASON_SHUFFLE_MASK 11
#define COMPTIME_REASON_DECL_NAME 41
+#define COMPTIME_REASON_INLINE_ASSEMBLY_CODE 20
+#define COMPTIME_REASON_CLOBBER 28
// Mirrors comptimeExpr2 (AstGen.zig:1982).
// Evaluates a node in a comptime block_comptime scope.
@@ -3281,6 +3285,239 @@ static uint32_t numberLiteral(
return result;
}
+// --- asmExpr (AstGen.zig:8791-8907) ---
+// Handles asm_simple / asm nodes. Emits a ZIR_EXT_ASM or ZIR_EXT_ASM_EXPR
+// extended instruction.
+static uint32_t asmExpr(
+ GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
+ AstGenCtx* ag = gz->astgen;
+ const Ast* tree = ag->tree;
+ AstNodeTag node_tag = tree->nodes.tags[node];
+ AstData nd = tree->nodes.datas[node];
+
+ // Decode asm node into components (Ast.zig:1889-1923).
+ uint32_t asm_token = tree->nodes.main_tokens[node];
+ uint32_t template_node;
+ uint32_t items_start = 0, items_end = 0;
+ uint32_t clobbers_node = 0; // 0 = none
+
+ if (node_tag == AST_NODE_ASM_SIMPLE) {
+ // asmSimple: data = node_and_token: lhs=template, rhs=rparen.
+ template_node = nd.lhs;
+ // items is empty, clobbers = none.
+ } else {
+ // asm: data = node_and_extra: lhs=template, rhs=extra_index.
+ // extra[rhs] = Asm { items_start, items_end, clobbers, rparen }
+ template_node = nd.lhs;
+ items_start = tree->extra_data.arr[nd.rhs];
+ items_end = tree->extra_data.arr[nd.rhs + 1];
+ uint32_t clobbers_raw = tree->extra_data.arr[nd.rhs + 2];
+ // OptionalIndex: UINT32_MAX = none, otherwise valid node index.
+ clobbers_node = (clobbers_raw != UINT32_MAX) ? clobbers_raw : 0;
+ }
+
+ // Resolve volatile_token (fullAsmComponents, Ast.zig:2285-2287).
+ uint32_t volatile_token = 0; // 0 = not volatile
+ if (tree->tokens.tags[asm_token + 1] == TOKEN_KEYWORD_VOLATILE) {
+ volatile_token = asm_token + 1;
+ }
+
+ // Split items into outputs and inputs (Ast.zig:2288-2296).
+ uint32_t items_len = items_end - items_start;
+ uint32_t outputs_end_idx = 0;
+ for (uint32_t i = 0; i < items_len; i++) {
+ uint32_t item = tree->extra_data.arr[items_start + i];
+ if (tree->nodes.tags[item] == AST_NODE_ASM_OUTPUT)
+ outputs_end_idx = i + 1;
+ else
+ break;
+ }
+ uint32_t outputs_len = outputs_end_idx;
+ uint32_t inputs_len = items_len - outputs_end_idx;
+
+ // Determine tag and template string (AstGen.zig:8801-8815).
+ uint16_t ext_tag;
+ uint32_t tmpl; // NullTerminatedString index into string_bytes
+ AstNodeTag tmpl_tag = tree->nodes.tags[template_node];
+ if (tmpl_tag == AST_NODE_STRING_LITERAL) {
+ ext_tag = (uint16_t)ZIR_EXT_ASM;
+ uint32_t str_lit_token = tree->nodes.main_tokens[template_node];
+ uint32_t si, sl;
+ strLitAsString(ag, str_lit_token, &si, &sl);
+ tmpl = si;
+ } else if (tmpl_tag == AST_NODE_MULTILINE_STRING_LITERAL) {
+ ext_tag = (uint16_t)ZIR_EXT_ASM;
+ // strLitNodeAsString equivalent: write string bytes for multiline.
+ AstData tmpl_data = tree->nodes.datas[template_node];
+ uint32_t start_tok = tmpl_data.lhs;
+ uint32_t end_tok = tmpl_data.rhs;
+ tmpl = ag->string_bytes_len;
+ for (uint32_t tok_i = start_tok; tok_i <= end_tok; tok_i++) {
+ uint32_t tok_start = tree->tokens.starts[tok_i];
+ const char* source = tree->source;
+ uint32_t content_start = tok_start + 2; // skip "\\"
+ uint32_t content_end = content_start;
+ while (
+ content_end < tree->source_len && source[content_end] != '\n')
+ content_end++;
+ uint32_t line_len = content_end - content_start;
+ if (tok_i > start_tok) {
+ ensureStringBytesCapacity(ag, line_len + 1);
+ ag->string_bytes[ag->string_bytes_len++] = '\n';
+ } else {
+ ensureStringBytesCapacity(ag, line_len);
+ }
+ memcpy(ag->string_bytes + ag->string_bytes_len,
+ source + content_start, line_len);
+ ag->string_bytes_len += line_len;
+ }
+ ensureStringBytesCapacity(ag, 1);
+ ag->string_bytes[ag->string_bytes_len++] = 0;
+ } else {
+ // Expression template: asm_expr (AstGen.zig:8811-8814).
+ ext_tag = (uint16_t)ZIR_EXT_ASM_EXPR;
+ tmpl = comptimeExpr(gz, scope, RL_NONE_VAL, template_node,
+ template_node, COMPTIME_REASON_INLINE_ASSEMBLY_CODE);
+ }
+
+ // Check container vs function asm (AstGen.zig:8820-8830).
+ bool is_container_asm = (ag->fn_block == NULL);
+ if (is_container_asm) {
+ if (volatile_token != 0) {
+ SET_ERROR(ag);
+ return ZIR_REF_VOID_VALUE;
+ }
+ if (outputs_len != 0 || inputs_len != 0 || clobbers_node != 0) {
+ SET_ERROR(ag);
+ return ZIR_REF_VOID_VALUE;
+ }
+ } else {
+ if (outputs_len == 0 && volatile_token == 0) {
+ SET_ERROR(ag);
+ return ZIR_REF_VOID_VALUE;
+ }
+ }
+
+ // Limit checks (AstGen.zig:8831-8833, 8870-8872).
+ if (outputs_len >= 16) {
+ SET_ERROR(ag);
+ return ZIR_REF_VOID_VALUE;
+ }
+ if (inputs_len >= 32) {
+ SET_ERROR(ag);
+ return ZIR_REF_VOID_VALUE;
+ }
+
+ // Evaluate all operands first, before writing extra data
+ // (AstGen.zig:8839-8906). Instruction-generating calls must complete
+ // before we append to ag->extra, since they also mutate ag->extra.
+
+ uint32_t output_type_bits = 0;
+
+ // Output buffers: each output has 3 words (name, constraint, operand).
+ // Max 15 outputs (checked above).
+ uint32_t output_buf[15 * 3];
+ for (uint32_t i = 0; i < outputs_len; i++) {
+ uint32_t output_node = tree->extra_data.arr[items_start + i];
+ uint32_t symbolic_name = tree->nodes.main_tokens[output_node];
+ uint32_t name = identAsString(ag, symbolic_name);
+ uint32_t constraint_token = symbolic_name + 2;
+ uint32_t c_si, c_sl;
+ strLitAsString(ag, constraint_token, &c_si, &c_sl);
+ uint32_t constraint = c_si;
+ bool has_arrow = (tree->tokens.tags[symbolic_name + 4] == TOKEN_ARROW);
+ uint32_t operand;
+ if (has_arrow) {
+ if (output_type_bits != 0) {
+ SET_ERROR(ag);
+ return ZIR_REF_VOID_VALUE;
+ }
+ output_type_bits |= (uint32_t)1 << i;
+ // out_type_node = nodeData(output_node).opt_node_and_token[0]
+ uint32_t out_type_node = tree->nodes.datas[output_node].lhs;
+ operand = typeExpr(gz, scope, out_type_node);
+ } else {
+ uint32_t ident_token = symbolic_name + 4;
+ operand = localVarRef(gz, scope, RL_REF_VAL, node, ident_token,
+ identAsString(ag, ident_token));
+ }
+ output_buf[i * 3 + 0] = name;
+ output_buf[i * 3 + 1] = constraint;
+ output_buf[i * 3 + 2] = operand;
+ }
+
+ // Input buffers: each input has 3 words (name, constraint, operand).
+ // Max 31 inputs (checked above).
+ uint32_t input_buf[31 * 3];
+ for (uint32_t i = 0; i < inputs_len; i++) {
+ uint32_t input_node
+ = tree->extra_data.arr[items_start + outputs_end_idx + i];
+ uint32_t symbolic_name = tree->nodes.main_tokens[input_node];
+ uint32_t name = identAsString(ag, symbolic_name);
+ uint32_t constraint_token = symbolic_name + 2;
+ uint32_t c_si, c_sl;
+ strLitAsString(ag, constraint_token, &c_si, &c_sl);
+ uint32_t constraint = c_si;
+ uint32_t operand_node = tree->nodes.datas[input_node].lhs;
+ uint32_t operand = expr(gz, scope, operand_node);
+ input_buf[i * 3 + 0] = name;
+ input_buf[i * 3 + 1] = constraint;
+ input_buf[i * 3 + 2] = operand;
+ }
+
+ // Evaluate clobbers (AstGen.zig:8889-8894).
+ uint32_t clobbers_ref;
+ if (clobbers_node != 0) {
+ uint32_t clobbers_type
+ = addBuiltinValue(gz, clobbers_node, ZIR_BUILTIN_VALUE_CLOBBERS);
+ ResultLoc clobbers_rl = { .tag = RL_COERCED_TY,
+ .data = clobbers_type,
+ .src_node = 0,
+ .ctx = RI_CTX_NONE };
+ clobbers_ref = comptimeExpr(gz, scope, clobbers_rl, clobbers_node,
+ clobbers_node, COMPTIME_REASON_CLOBBER);
+ } else {
+ clobbers_ref = ZIR_REF_NONE;
+ }
+
+ // Now write all extra data at once (AstGen.zig:8855-8866).
+ uint32_t total_extra = 4 + outputs_len * 3 + inputs_len * 3;
+ ensureExtraCapacity(ag, total_extra);
+
+ uint32_t payload_index = ag->extra_len;
+ // Zir.Inst.Asm header (4 words):
+ ag->extra[ag->extra_len++]
+ = (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
+ ag->extra[ag->extra_len++] = tmpl;
+ ag->extra[ag->extra_len++] = output_type_bits;
+ ag->extra[ag->extra_len++] = clobbers_ref;
+ // Outputs (3 words each).
+ for (uint32_t i = 0; i < outputs_len * 3; i++)
+ ag->extra[ag->extra_len++] = output_buf[i];
+ // Inputs (3 words each).
+ for (uint32_t i = 0; i < inputs_len * 3; i++)
+ ag->extra[ag->extra_len++] = input_buf[i];
+
+ // Emit the extended instruction (AstGen.zig:8868-8884).
+ // Small = packed { is_volatile: bool, outputs_len: u7, inputs_len: u8 }
+ uint16_t small = (uint16_t)(((volatile_token != 0) ? 1 : 0)
+ | ((outputs_len & 0x7F) << 1) | ((inputs_len & 0xFF) << 8));
+
+ ensureInstCapacity(ag, 1);
+ uint32_t idx = ag->inst_len;
+ ag->inst_tags[idx] = ZIR_INST_EXTENDED;
+ ZirInstData data;
+ data.extended.opcode = ext_tag;
+ data.extended.small = small;
+ data.extended.operand = payload_index;
+ ag->inst_datas[idx] = data;
+ ag->inst_len++;
+ gzAppendInstruction(gz, idx);
+ uint32_t result = idx + ZIR_REF_START_INDEX;
+
+ return rvalue(gz, rl, result, node);
+}
+
// Mirrors builtinCall (AstGen.zig:9191), @import case (AstGen.zig:9242).
static uint32_t builtinCallImport(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
@@ -6982,6 +7219,13 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
return fieldAccessExpr(gz, scope, rl, node);
case AST_NODE_IDENTIFIER:
return identifierExpr(gz, scope, rl, node);
+ // asm (AstGen.zig:811-819).
+ case AST_NODE_ASM_SIMPLE:
+ case AST_NODE_ASM:
+ return asmExpr(gz, scope, rl, node);
+ case AST_NODE_ASM_LEGACY:
+ SET_ERROR(ag); // "legacy asm clobbers syntax"
+ return ZIR_REF_VOID_VALUE;
case AST_NODE_STRING_LITERAL: {
// Mirrors stringLiteral (AstGen.zig:8626).
uint32_t str_lit_token = ag->tree->nodes.main_tokens[node];
@@ -13634,7 +13878,20 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
type_gz.is_comptime = true;
type_gz.instructions_top = ag->scratch_inst_len;
type_gz.any_defer_node = UINT32_MAX;
- // type_gz body is filled later (extern only). Record top after.
+ // For extern fn: emit fn type via fnProtoExprInner into type_gz
+ // (AstGen.zig:4160-4164).
+ if (body_node == 0) {
+ ResultLoc rl_none
+ = { .tag = RL_NONE, .data = 0, .src_node = 0, .ctx = RI_CTX_NONE };
+ uint32_t type_inst = fnProtoExprInner(
+ &type_gz, &type_gz.base, rl_none, proto_node, true);
+ if (ag->has_compile_errors) {
+ gzUnstack(&type_gz);
+ return;
+ }
+ makeBreakInline(&type_gz, decl_inst, type_inst,
+ (int32_t)node - (int32_t)proto_node);
+ }
uint32_t type_top = ag->scratch_inst_len;
// align_gz sub-block (AstGen.zig:4166-4173).
@@ -13724,332 +13981,407 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
decl_gz.break_block = UINT32_MAX;
decl_gz.any_defer_node = UINT32_MAX;
- // --- Parameter iteration (AstGen.zig:4260-4363) ---
- // Walk params, creating param instructions and ScopeLocalVal entries.
- // We keep param scopes on the C stack.
- Scope* params_scope = &decl_gz.base;
- ScopeLocalVal param_scopes[256];
- uint32_t param_scope_count = 0;
- // Collect param instruction indices (AstGen.zig:4254, 4360).
- uint32_t param_insts[256];
- uint32_t param_insts_len = 0;
- // noalias_bits tracking (AstGen.zig:4259).
- uint32_t noalias_bits = 0;
- // Generic parameter/return type tracking (AstGen.zig:4257).
- bool any_param_used = false;
- // is_var_args detection (AstGen.zig:4261).
- bool is_var_args = false;
-
- // Parameter iteration using token-based iterator, mirroring upstream
- // FnProto.Iterator (Ast.zig:2680-2768, AstGen.zig:4260-4363).
- // The params array only contains type-expression params; anytype params
- // exist as tokens between/after the type-expression nodes.
- uint32_t lparen = fn_name_token + 1; // '(' token
- uint32_t iter_tok_i = lparen + 1; // first token after '('
- bool iter_tok_flag = true; // start in token-scanning mode
- uint32_t iter_param_i = 0;
- ResultLoc coerced_type_ri = { .tag = RL_COERCED_TY,
- .data = ZIR_REF_TYPE_TYPE,
- .src_node = 0,
- .ctx = RI_CTX_NONE };
-
- uint32_t param_type_i = 0; // index for noalias_bits (AstGen.zig:4262)
- while (true) {
- uint32_t name_token = 0;
- uint32_t comptime_noalias_token = 0;
- bool is_comptime_param = false;
- bool is_anytype = false;
- uint32_t param_type_node = 0;
+ // For non-extern: process params, return type, calling convention, body
+ // (AstGen.zig:4197-4201, fnDeclInner). For extern, this is already handled
+ // by fnProtoExprInner in type_gz above.
+ if (body_node != 0) {
+
+ // --- Parameter iteration (AstGen.zig:4260-4363) ---
+ // Walk params, creating param instructions and ScopeLocalVal entries.
+ // We keep param scopes on the C stack.
+ Scope* params_scope = &decl_gz.base;
+ ScopeLocalVal param_scopes[256];
+ uint32_t param_scope_count = 0;
+ // Collect param instruction indices (AstGen.zig:4254, 4360).
+ uint32_t param_insts[256];
+ uint32_t param_insts_len = 0;
+ // noalias_bits tracking (AstGen.zig:4259).
+ uint32_t noalias_bits = 0;
+ // Generic parameter/return type tracking (AstGen.zig:4257).
+ bool any_param_used = false;
+ // is_var_args detection (AstGen.zig:4261).
+ bool is_var_args = false;
+
+ // Parameter iteration using token-based iterator, mirroring upstream
+ // FnProto.Iterator (Ast.zig:2680-2768, AstGen.zig:4260-4363).
+ // The params array only contains type-expression params; anytype
+ // params exist as tokens between/after the type-expression nodes.
+ uint32_t lparen = fn_name_token + 1; // '(' token
+ uint32_t iter_tok_i = lparen + 1; // first token after '('
+ bool iter_tok_flag = true; // start in token-scanning mode
+ uint32_t iter_param_i = 0;
+ ResultLoc coerced_type_ri = { .tag = RL_COERCED_TY,
+ .data = ZIR_REF_TYPE_TYPE,
+ .src_node = 0,
+ .ctx = RI_CTX_NONE };
- if (!iter_tok_flag) {
- // Return next param from params array.
- if (iter_param_i >= params_len)
- break;
- param_type_node = param_nodes[iter_param_i];
- // Scan backwards from type expression to find
- // name/comptime/noalias (Ast.zig:2698-2705).
- uint32_t tok_i = firstToken(tree, param_type_node);
- while (tok_i > 0) {
- tok_i--;
- uint32_t ttag = tree->tokens.tags[tok_i];
- if (ttag == TOKEN_COLON)
- continue;
- if (ttag == TOKEN_IDENTIFIER) {
- name_token = tok_i;
- continue;
+ uint32_t param_type_i = 0; // index for noalias_bits (AstGen.zig:4262)
+ while (true) {
+ uint32_t name_token = 0;
+ uint32_t comptime_noalias_token = 0;
+ bool is_comptime_param = false;
+ bool is_anytype = false;
+ uint32_t param_type_node = 0;
+
+ if (!iter_tok_flag) {
+ // Return next param from params array.
+ if (iter_param_i >= params_len)
+ break;
+ param_type_node = param_nodes[iter_param_i];
+ // Scan backwards from type expression to find
+ // name/comptime/noalias (Ast.zig:2698-2705).
+ uint32_t tok_i = firstToken(tree, param_type_node);
+ while (tok_i > 0) {
+ tok_i--;
+ uint32_t ttag = tree->tokens.tags[tok_i];
+ if (ttag == TOKEN_COLON)
+ continue;
+ if (ttag == TOKEN_IDENTIFIER) {
+ name_token = tok_i;
+ continue;
+ }
+ if (ttag == TOKEN_KEYWORD_COMPTIME
+ || ttag == TOKEN_KEYWORD_NOALIAS) {
+ comptime_noalias_token = tok_i;
+ continue;
+ }
+ break;
}
- if (ttag == TOKEN_KEYWORD_COMPTIME
- || ttag == TOKEN_KEYWORD_NOALIAS) {
- comptime_noalias_token = tok_i;
+ iter_param_i++;
+ iter_tok_i = lastToken(tree, param_type_node) + 1;
+ // Skip comma after param for anytype scanning.
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_COMMA)
+ iter_tok_i++;
+ iter_tok_flag = true;
+ } else {
+ // Token-scanning mode: look for anytype/ellipsis params
+ // (Ast.zig:2721-2767).
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_COMMA)
+ iter_tok_i++;
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_R_PAREN)
+ break;
+ // Skip doc comments.
+ while (tree->tokens.tags[iter_tok_i] == TOKEN_DOC_COMMENT)
+ iter_tok_i++;
+ // Check for ellipsis3 (varargs) (AstGen.zig:4275-4281).
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_ELLIPSIS3) {
+ is_var_args = true;
+ break;
+ }
+ // Check for comptime/noalias prefix.
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_COMPTIME
+ || tree->tokens.tags[iter_tok_i]
+ == TOKEN_KEYWORD_NOALIAS) {
+ comptime_noalias_token = iter_tok_i;
+ iter_tok_i++;
+ }
+ // Check for name: identifier followed by colon.
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_IDENTIFIER
+ && tree->tokens.tags[iter_tok_i + 1] == TOKEN_COLON) {
+ name_token = iter_tok_i;
+ iter_tok_i += 2;
+ }
+ // Check for anytype keyword.
+ if (tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_ANYTYPE) {
+ is_anytype = true;
+ iter_tok_i++;
+ } else {
+ // Not an anytype param; switch to param-array mode.
+ iter_tok_flag = false;
continue;
}
- break;
}
- iter_param_i++;
- iter_tok_i = lastToken(tree, param_type_node) + 1;
- // Skip comma after param for anytype scanning.
- if (tree->tokens.tags[iter_tok_i] == TOKEN_COMMA)
- iter_tok_i++;
- iter_tok_flag = true;
- } else {
- // Token-scanning mode: look for anytype/ellipsis params
- // (Ast.zig:2721-2767).
- if (tree->tokens.tags[iter_tok_i] == TOKEN_COMMA)
- iter_tok_i++;
- if (tree->tokens.tags[iter_tok_i] == TOKEN_R_PAREN)
- break;
- // Skip doc comments.
- while (tree->tokens.tags[iter_tok_i] == TOKEN_DOC_COMMENT)
- iter_tok_i++;
- // Check for ellipsis3 (varargs) (AstGen.zig:4275-4281).
- if (tree->tokens.tags[iter_tok_i] == TOKEN_ELLIPSIS3) {
- is_var_args = true;
- break;
- }
- // Check for comptime/noalias prefix.
- if (tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_COMPTIME
- || tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_NOALIAS) {
- comptime_noalias_token = iter_tok_i;
- iter_tok_i++;
- }
- // Check for name: identifier followed by colon.
- if (tree->tokens.tags[iter_tok_i] == TOKEN_IDENTIFIER
- && tree->tokens.tags[iter_tok_i + 1] == TOKEN_COLON) {
- name_token = iter_tok_i;
- iter_tok_i += 2;
+
+ // Determine is_comptime and noalias from comptime_noalias token
+ // (AstGen.zig:4265-4273).
+ if (comptime_noalias_token != 0
+ && tree->tokens.tags[comptime_noalias_token]
+ == TOKEN_KEYWORD_NOALIAS) {
+ // Track noalias_bits (AstGen.zig:4266-4269).
+ if (param_type_i < 32)
+ noalias_bits |= (1u << param_type_i);
}
- // Check for anytype keyword.
- if (tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_ANYTYPE) {
- is_anytype = true;
- iter_tok_i++;
- } else {
- // Not an anytype param; switch to param-array mode.
- iter_tok_flag = false;
- continue;
+ if (comptime_noalias_token != 0
+ && tree->tokens.tags[comptime_noalias_token]
+ == TOKEN_KEYWORD_COMPTIME) {
+ is_comptime_param = true;
}
- }
- // Determine is_comptime and noalias from comptime_noalias token
- // (AstGen.zig:4265-4273).
- if (comptime_noalias_token != 0
- && tree->tokens.tags[comptime_noalias_token]
- == TOKEN_KEYWORD_NOALIAS) {
- // Track noalias_bits (AstGen.zig:4266-4269).
- if (param_type_i < 32)
- noalias_bits |= (1u << param_type_i);
- }
- if (comptime_noalias_token != 0
- && tree->tokens.tags[comptime_noalias_token]
- == TOKEN_KEYWORD_COMPTIME) {
- is_comptime_param = true;
- }
-
- // Determine param name string (AstGen.zig:4283-4321).
- // Must be resolved BEFORE type expression to match upstream string
- // table ordering.
- uint32_t param_name_str = 0; // NullTerminatedString.empty
- if (name_token != 0) {
- uint32_t name_start = tree->tokens.starts[name_token];
- char nch = tree->source[name_start];
- // Skip "_" params (AstGen.zig:4285-4286).
- if (nch == '_') {
- uint32_t next_start = tree->tokens.starts[name_token + 1];
- if (next_start == name_start + 1) {
- // Single underscore: empty name.
- param_name_str = 0;
+ // Determine param name string (AstGen.zig:4283-4321).
+ // Must be resolved BEFORE type expression to match upstream string
+ // table ordering.
+ uint32_t param_name_str = 0; // NullTerminatedString.empty
+ if (name_token != 0) {
+ uint32_t name_start = tree->tokens.starts[name_token];
+ char nch = tree->source[name_start];
+ // Skip "_" params (AstGen.zig:4285-4286).
+ if (nch == '_') {
+ uint32_t next_start = tree->tokens.starts[name_token + 1];
+ if (next_start == name_start + 1) {
+ // Single underscore: empty name.
+ param_name_str = 0;
+ } else {
+ param_name_str = identAsString(ag, name_token);
+ }
} else {
param_name_str = identAsString(ag, name_token);
}
+ }
+
+ // Emit param instruction (AstGen.zig:4323-4345).
+ uint32_t param_inst_ref;
+ if (is_anytype) {
+ // anytype parameter: emit param_anytype/param_anytype_comptime
+ // (AstGen.zig:4323-4329).
+ uint32_t anytype_name_token
+ = name_token != 0 ? name_token : (iter_tok_i - 1);
+ ZirInstTag anytype_tag = is_comptime_param
+ ? ZIR_INST_PARAM_ANYTYPE_COMPTIME
+ : ZIR_INST_PARAM_ANYTYPE;
+ uint32_t anytype_inst = addStrTok(
+ &decl_gz, anytype_tag, param_name_str, anytype_name_token);
+ param_inst_ref = anytype_inst; // already a ref (toRef())
+ if (param_insts_len < 256)
+ param_insts[param_insts_len++]
+ = anytype_inst - ZIR_REF_START_INDEX; // toIndex()
} else {
- param_name_str = identAsString(ag, name_token);
+ // Type-expression parameter (AstGen.zig:4330-4344).
+ any_param_used = false; // reset before evaluating type body
+ GenZir param_gz = makeSubBlock(&decl_gz, params_scope);
+ uint32_t param_type_ref = fullBodyExpr(
+ ¶m_gz, params_scope, coerced_type_ri, param_type_node);
+
+ if (ag->has_compile_errors)
+ return;
+
+ // The break_inline target is the param instruction we're about
+ // to create (AstGen.zig:4336-4337).
+ uint32_t param_inst_expected = ag->inst_len + 1;
+ makeBreakInline(¶m_gz, param_inst_expected, param_type_ref,
+ (int32_t)param_type_node
+ - (int32_t)param_gz.decl_node_index);
+ bool param_type_is_generic = any_param_used;
+
+ // Create param instruction (AstGen.zig:4341-4343).
+ ZirInstTag param_tag = is_comptime_param
+ ? ZIR_INST_PARAM_COMPTIME
+ : ZIR_INST_PARAM;
+ uint32_t name_tok_for_src = name_token != 0
+ ? name_token
+ : tree->nodes.main_tokens[param_type_node];
+ uint32_t param_inst = addParam(&decl_gz, ¶m_gz,
+ param_insts, param_insts_len, param_tag, name_tok_for_src,
+ param_name_str, param_type_is_generic);
+ (void)param_inst_expected;
+ param_inst_ref = param_inst + ZIR_REF_START_INDEX;
+ if (param_insts_len < 256)
+ param_insts[param_insts_len++] = param_inst;
}
- }
- // Emit param instruction (AstGen.zig:4323-4345).
- uint32_t param_inst_ref;
- if (is_anytype) {
- // anytype parameter: emit param_anytype/param_anytype_comptime
- // (AstGen.zig:4323-4329).
- uint32_t anytype_name_token
- = name_token != 0 ? name_token : (iter_tok_i - 1);
- ZirInstTag anytype_tag = is_comptime_param
- ? ZIR_INST_PARAM_ANYTYPE_COMPTIME
- : ZIR_INST_PARAM_ANYTYPE;
- uint32_t anytype_inst = addStrTok(
- &decl_gz, anytype_tag, param_name_str, anytype_name_token);
- param_inst_ref = anytype_inst; // already a ref (toRef())
- if (param_insts_len < 256)
- param_insts[param_insts_len++]
- = anytype_inst - ZIR_REF_START_INDEX; // toIndex()
- } else {
- // Type-expression parameter (AstGen.zig:4330-4344).
- any_param_used = false; // reset before evaluating type body
- GenZir param_gz = makeSubBlock(&decl_gz, params_scope);
- uint32_t param_type_ref = fullBodyExpr(
- ¶m_gz, params_scope, coerced_type_ri, param_type_node);
+ // Create ScopeLocalVal for this param (AstGen.zig:4349-4359).
+ if (param_name_str != 0 && param_scope_count < 256) {
+ ScopeLocalVal* lv = ¶m_scopes[param_scope_count++];
+ lv->base.tag = SCOPE_LOCAL_VAL;
+ lv->parent = params_scope;
+ lv->gen_zir = &decl_gz;
+ lv->inst = param_inst_ref;
+ lv->token_src = name_token;
+ lv->name = param_name_str;
+ lv->is_used_or_discarded = &any_param_used;
+ params_scope = &lv->base;
+ }
+ param_type_i++;
+ }
+ // --- Return type (AstGen.zig:4369-4383) ---
+ any_param_used = false; // reset for return type generic detection
+ GenZir ret_gz = makeSubBlock(&decl_gz, params_scope);
+ uint32_t ret_ref = ZIR_REF_NONE;
+ if (return_type_node != 0) {
+ ret_ref = fullBodyExpr(
+ &ret_gz, params_scope, coerced_type_ri, return_type_node);
if (ag->has_compile_errors)
return;
+ // If ret_gz produced instructions, add break_inline
+ // (AstGen.zig:4377-4381).
+ if (gzInstructionsLen(&ret_gz) > 0) {
+ // break_inline targets the func instruction (which doesn't
+ // exist yet). We use 0 as placeholder and patch later.
+ makeBreakInline(&ret_gz, 0, ret_ref, AST_NODE_OFFSET_NONE);
+ }
+ }
+ // Fetch ref entries for params used in return type (AstGen.zig:4384).
+ uint32_t ret_param_refs[32];
+ uint32_t ret_param_refs_len = fetchRemoveRefEntries(
+ ag, param_insts, param_insts_len, ret_param_refs, 32);
+ bool ret_ty_is_generic = any_param_used;
+ // Save original ret_ref for fn_ret_ty (AstGen.zig:4449 uses raw
+ // ret_ref).
+ uint32_t ret_ref_raw = ret_ref;
+ // Map void_type → .none (AstGen.zig:12054) — only for addFunc payload.
+ if (ret_ref == ZIR_REF_VOID_TYPE)
+ ret_ref = ZIR_REF_NONE;
+
+ uint32_t ret_body_len = gzInstructionsLen(&ret_gz);
+ // Copy ret_body before unstacking: body_gz reuses the same scratch
+ // area.
+ uint32_t* ret_body = NULL;
+ if (ret_body_len > 0) {
+ ret_body = malloc(ret_body_len * sizeof(uint32_t));
+ if (!ret_body)
+ abort();
+ memcpy(ret_body, gzInstructionsSlice(&ret_gz),
+ ret_body_len * sizeof(uint32_t));
+ }
+ gzUnstack(&ret_gz);
- // The break_inline target is the param instruction we're about
- // to create (AstGen.zig:4336-4337).
- uint32_t param_inst_expected = ag->inst_len + 1;
- makeBreakInline(¶m_gz, param_inst_expected, param_type_ref,
- (int32_t)param_type_node - (int32_t)param_gz.decl_node_index);
- bool param_type_is_generic = any_param_used;
+ // Restore source cursor (AstGen.zig:4387-4388).
+ ag->source_offset = saved_source_offset;
+ ag->source_line = saved_source_line;
+ ag->source_column = saved_source_column;
- // Create param instruction (AstGen.zig:4341-4343).
- ZirInstTag param_tag
- = is_comptime_param ? ZIR_INST_PARAM_COMPTIME : ZIR_INST_PARAM;
- uint32_t name_tok_for_src = name_token != 0
- ? name_token
- : tree->nodes.main_tokens[param_type_node];
- uint32_t param_inst = addParam(&decl_gz, ¶m_gz, param_insts,
- param_insts_len, param_tag, name_tok_for_src, param_name_str,
- param_type_is_generic);
- (void)param_inst_expected;
- param_inst_ref = param_inst + ZIR_REF_START_INDEX;
- if (param_insts_len < 256)
- param_insts[param_insts_len++] = param_inst;
- }
-
- // Create ScopeLocalVal for this param (AstGen.zig:4349-4359).
- if (param_name_str != 0 && param_scope_count < 256) {
- ScopeLocalVal* lv = ¶m_scopes[param_scope_count++];
- lv->base.tag = SCOPE_LOCAL_VAL;
- lv->parent = params_scope;
- lv->gen_zir = &decl_gz;
- lv->inst = param_inst_ref;
- lv->token_src = name_token;
- lv->name = param_name_str;
- lv->is_used_or_discarded = &any_param_used;
- params_scope = &lv->base;
+ // --- Calling convention (AstGen.zig:4390-4413) ---
+ // Note: cc_gz uses `scope` (= &decl_gz.base), not params_scope.
+ GenZir cc_gz = makeSubBlock(&decl_gz, &decl_gz.base);
+ uint32_t cc_ref = ZIR_REF_NONE;
+ if (callconv_expr_node != 0) {
+ // Explicit callconv(expr) (AstGen.zig:4393-4405).
+ uint32_t cc_ty = addBuiltinValue(&cc_gz, callconv_expr_node,
+ ZIR_BUILTIN_VALUE_CALLING_CONVENTION);
+ ResultLoc cc_ri = { .tag = RL_COERCED_TY,
+ .data = cc_ty,
+ .src_node = 0,
+ .ctx = RI_CTX_NONE };
+ cc_ref = exprRl(&cc_gz, &decl_gz.base, cc_ri, callconv_expr_node);
+ if (ag->has_compile_errors) {
+ free(ret_body);
+ return;
+ }
+ if (gzInstructionsLen(&cc_gz) > 0) {
+ // break_inline targets the func instruction (patched later).
+ makeBreakInline(&cc_gz, 0, cc_ref, AST_NODE_OFFSET_NONE);
+ }
+ } else if (has_inline_keyword) {
+ // inline keyword → calling_convention_inline
+ // (AstGen.zig:4406-4409).
+ cc_ref = addBuiltinValue(
+ &cc_gz, node, ZIR_BUILTIN_VALUE_CALLING_CONVENTION_INLINE);
+ makeBreakInline(&cc_gz, 0, cc_ref, AST_NODE_OFFSET_NONE);
}
- param_type_i++;
- }
- // --- Return type (AstGen.zig:4369-4383) ---
- any_param_used = false; // reset for return type generic detection
- GenZir ret_gz = makeSubBlock(&decl_gz, params_scope);
- uint32_t ret_ref = ZIR_REF_NONE;
- if (return_type_node != 0) {
- ret_ref = fullBodyExpr(
- &ret_gz, params_scope, coerced_type_ri, return_type_node);
- if (ag->has_compile_errors)
- return;
- // If ret_gz produced instructions, add break_inline
- // (AstGen.zig:4377-4381).
- if (gzInstructionsLen(&ret_gz) > 0) {
- // break_inline targets the func instruction (which doesn't
- // exist yet). We use 0 as placeholder and patch later.
- makeBreakInline(&ret_gz, 0, ret_ref, AST_NODE_OFFSET_NONE);
- }
- }
- // Fetch ref entries for params used in return type (AstGen.zig:4384).
- uint32_t ret_param_refs[32];
- uint32_t ret_param_refs_len = fetchRemoveRefEntries(
- ag, param_insts, param_insts_len, ret_param_refs, 32);
- bool ret_ty_is_generic = any_param_used;
- // Save original ret_ref for fn_ret_ty (AstGen.zig:4449 uses raw ret_ref).
- uint32_t ret_ref_raw = ret_ref;
- // Map void_type → .none (AstGen.zig:12054) — only for addFunc payload.
- if (ret_ref == ZIR_REF_VOID_TYPE)
- ret_ref = ZIR_REF_NONE;
-
- uint32_t ret_body_len = gzInstructionsLen(&ret_gz);
- // Copy ret_body before unstacking: body_gz reuses the same scratch area.
- uint32_t* ret_body = NULL;
- if (ret_body_len > 0) {
- ret_body = malloc(ret_body_len * sizeof(uint32_t));
- if (!ret_body)
- abort();
- memcpy(ret_body, gzInstructionsSlice(&ret_gz),
- ret_body_len * sizeof(uint32_t));
- }
- gzUnstack(&ret_gz);
+ uint32_t cc_body_len = gzInstructionsLen(&cc_gz);
+ uint32_t* cc_body = NULL;
+ if (cc_body_len > 0) {
+ cc_body = malloc(cc_body_len * sizeof(uint32_t));
+ if (!cc_body)
+ abort();
+ memcpy(cc_body, gzInstructionsSlice(&cc_gz),
+ cc_body_len * sizeof(uint32_t));
+ }
+ gzUnstack(&cc_gz);
+
+ // --- Body (AstGen.zig:4415-4424) ---
+ GenZir body_gz;
+ memset(&body_gz, 0, sizeof(body_gz));
+ body_gz.base.tag = SCOPE_GEN_ZIR;
+ body_gz.parent = params_scope;
+ body_gz.astgen = ag;
+ body_gz.decl_node_index = proto_node;
+ body_gz.decl_line = decl_line;
+ body_gz.is_comptime = false;
+ body_gz.instructions_top = ag->scratch_inst_len;
+ body_gz.any_defer_node = UINT32_MAX;
+
+ // Set fn_block, fn_ret_ty, fn_var_args for the body
+ // (AstGen.zig:4442-4459).
+ void* prev_fn_block = ag->fn_block;
+ setFnBlock(ag, &body_gz);
+ uint32_t prev_fn_ret_ty = ag->fn_ret_ty;
+ bool prev_fn_var_args = ag->fn_var_args;
+ ag->fn_var_args = is_var_args;
+ // Use ret_ref_raw (before void_type→none mapping) to match upstream
+ // AstGen.zig:4449: fn_ret_ty = if (is_inferred_error or
+ // ret_ref.toIndex() != null) body_gz.addNode(.ret_type) else ret_ref.
+ if (is_inferred_error || ret_ref_raw >= ZIR_REF_START_INDEX) {
+ ZirInstData rtdata;
+ memset(&rtdata, 0, sizeof(rtdata));
+ rtdata.node = (int32_t)node - (int32_t)body_gz.decl_node_index;
+ ag->fn_ret_ty
+ = addInstruction(&body_gz, ZIR_INST_RET_TYPE, rtdata);
+ } else {
+ ag->fn_ret_ty = ret_ref_raw;
+ }
- // Restore source cursor (AstGen.zig:4387-4388).
- ag->source_offset = saved_source_offset;
- ag->source_line = saved_source_line;
- ag->source_column = saved_source_column;
+ // Process function body (AstGen.zig:4461-4465).
+ advanceSourceCursorToNode(ag, body_node);
+ uint32_t lbrace_line = ag->source_line - decl_line;
+ uint32_t lbrace_column = ag->source_column;
+
+ fullBodyExpr(&body_gz, &body_gz.base, RL_NONE_VAL, body_node);
+
+ ag->within_fn = prev_within_fn;
+ ag->fn_block = prev_fn_block;
+ ag->fn_ret_ty = prev_fn_ret_ty;
+ ag->fn_var_args = prev_fn_var_args;
- // --- Calling convention (AstGen.zig:4390-4413) ---
- // Note: cc_gz uses `scope` (= &decl_gz.base), not params_scope.
- GenZir cc_gz = makeSubBlock(&decl_gz, &decl_gz.base);
- uint32_t cc_ref = ZIR_REF_NONE;
- if (callconv_expr_node != 0) {
- // Explicit callconv(expr) (AstGen.zig:4393-4405).
- uint32_t cc_ty = addBuiltinValue(
- &cc_gz, callconv_expr_node, ZIR_BUILTIN_VALUE_CALLING_CONVENTION);
- ResultLoc cc_ri = { .tag = RL_COERCED_TY,
- .data = cc_ty,
- .src_node = 0,
- .ctx = RI_CTX_NONE };
- cc_ref = exprRl(&cc_gz, &decl_gz.base, cc_ri, callconv_expr_node);
if (ag->has_compile_errors) {
free(ret_body);
+ free(cc_body);
return;
}
- if (gzInstructionsLen(&cc_gz) > 0) {
- // break_inline targets the func instruction (patched later).
- makeBreakInline(&cc_gz, 0, cc_ref, AST_NODE_OFFSET_NONE);
- }
- } else if (has_inline_keyword) {
- // inline keyword → calling_convention_inline
- // (AstGen.zig:4406-4409).
- cc_ref = addBuiltinValue(
- &cc_gz, node, ZIR_BUILTIN_VALUE_CALLING_CONVENTION_INLINE);
- makeBreakInline(&cc_gz, 0, cc_ref, AST_NODE_OFFSET_NONE);
- }
- uint32_t cc_body_len = gzInstructionsLen(&cc_gz);
- uint32_t* cc_body = NULL;
- if (cc_body_len > 0) {
- cc_body = malloc(cc_body_len * sizeof(uint32_t));
- if (!cc_body)
- abort();
- memcpy(cc_body, gzInstructionsSlice(&cc_gz),
- cc_body_len * sizeof(uint32_t));
- }
- gzUnstack(&cc_gz);
-
- // --- Body handling ---
- // For fn_proto* (no body): extern function type
- // (AstGen.zig:4136-4164).
- // For fn_decl (has body): emit function value (AstGen.zig:4197-4201).
- uint32_t func_ref;
- if (body_node == 0) {
- // fn_proto without body: extern function type.
- // Upstream calls fnProtoExprInner with implicit_ccc=true.
- // We use the already-computed params/ret but add implicit CCC
- // if no explicit callconv was provided (AstGen.zig:1394-1396).
- if (cc_ref == ZIR_REF_NONE && !has_inline_keyword) {
- // Extern fn without explicit callconv: implicit C calling
- // convention (AstGen.zig:1394-1396 via fnProtoExprInner).
- cc_ref = addBuiltinValue(
- &decl_gz, node, ZIR_BUILTIN_VALUE_CALLING_CONVENTION_C);
- }
- // Emit func instruction with no body (AstGen.zig:1402-1422).
- bool need_fancy
- = cc_ref != ZIR_REF_NONE || is_var_args || noalias_bits != 0;
+ // Add implicit return at end of function body
+ // (AstGen.zig:4465-4871).
+ if (!endsWithNoReturn(&body_gz)) {
+ ZirInstData rdata;
+ rdata.un_node.operand = ZIR_REF_NONE;
+ rdata.un_node.src_node
+ = (int32_t)node - (int32_t)body_gz.decl_node_index;
+ addInstruction(
+ &body_gz, ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL, rdata);
+
+ uint32_t body_last_tok = lastToken(tree, body_node);
+ ZirInstData rdata2;
+ rdata2.un_tok.operand = ZIR_REF_VOID_VALUE;
+ rdata2.un_tok.src_tok
+ = tokenIndexToRelative(&body_gz, body_last_tok);
+ addInstruction(&body_gz, ZIR_INST_RET_IMPLICIT, rdata2);
+ }
+
+ // Read body before unstacking (AstGen.zig:12215-12218).
+ const uint32_t* fn_body = gzInstructionsSlice(&body_gz);
+ uint32_t fn_body_len = gzInstructionsLen(&body_gz);
+ gzUnstack(&body_gz);
+
+ // Create func/func_fancy instruction (AstGen.zig:4476-4494,
+ // 12112-12173).
+ uint32_t func_ref;
+ bool need_fancy = cc_ref != ZIR_REF_NONE || is_var_args
+ || noalias_bits != 0 || is_noinline;
if (need_fancy) {
- func_ref = addFuncFancy(&decl_gz, node, 0, decl_inst, ret_ref,
- ret_body, ret_body_len, cc_ref, cc_body, cc_body_len, NULL, 0,
- param_insts, param_insts_len, 0, 0, is_var_args, false, false,
- noalias_bits, ret_ty_is_generic, ret_param_refs,
+ func_ref = addFuncFancy(&decl_gz, node, body_node, decl_inst,
+ ret_ref, ret_body, ret_body_len, cc_ref, cc_body, cc_body_len,
+ fn_body, fn_body_len, param_insts, param_insts_len,
+ lbrace_line, lbrace_column, is_var_args, is_inferred_error,
+ is_noinline, noalias_bits, ret_ty_is_generic, ret_param_refs,
ret_param_refs_len);
} else {
- func_ref = addFunc(&decl_gz, node, 0, decl_inst, ret_ref, ret_body,
- ret_body_len, NULL, 0, param_insts, param_insts_len, 0, 0,
- false, ret_ty_is_generic, ret_param_refs, ret_param_refs_len);
+ func_ref = addFunc(&decl_gz, node, body_node, decl_inst, ret_ref,
+ ret_body, ret_body_len, fn_body, fn_body_len, param_insts,
+ param_insts_len, lbrace_line, lbrace_column, is_inferred_error,
+ ret_ty_is_generic, ret_param_refs, ret_param_refs_len);
}
- // Patch ret_body/cc_body break_inlines (AstGen.zig:12199-12202).
+
+ // Patch ret_body break_inline to point to func instruction
+ // (AstGen.zig:12199-12202).
if (ret_body_len > 0) {
uint32_t break_inst = ret_body[ret_body_len - 1];
uint32_t break_payload
= ag->inst_datas[break_inst].break_data.payload_index;
ag->extra[break_payload + 1] = func_ref - ZIR_REF_START_INDEX;
}
+ // Patch cc_body break_inline to point to func instruction
+ // (AstGen.zig:12146-12148).
if (cc_body_len > 0) {
uint32_t break_inst = cc_body[cc_body_len - 1];
uint32_t break_payload
@@ -14058,128 +14390,16 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
}
free(ret_body);
free(cc_body);
- // break_inline from decl_gz to decl_inst (AstGen.zig:4163).
+
+ // break_inline returning func to declaration (AstGen.zig:4495).
+ // nodeIndexToRelative(decl_node) = node - decl_gz.decl_node_index.
makeBreakInline(&decl_gz, decl_inst, func_ref,
(int32_t)node - (int32_t)proto_node);
- goto fn_decl_finish;
- }
-
- // --- Body (AstGen.zig:4415-4424) ---
- GenZir body_gz;
- memset(&body_gz, 0, sizeof(body_gz));
- body_gz.base.tag = SCOPE_GEN_ZIR;
- body_gz.parent = params_scope;
- body_gz.astgen = ag;
- body_gz.decl_node_index = proto_node;
- body_gz.decl_line = decl_line;
- body_gz.is_comptime = false;
- body_gz.instructions_top = ag->scratch_inst_len;
- body_gz.any_defer_node = UINT32_MAX;
-
- // Set fn_block, fn_ret_ty, fn_var_args for the body
- // (AstGen.zig:4442-4459).
- void* prev_fn_block = ag->fn_block;
- setFnBlock(ag, &body_gz);
- uint32_t prev_fn_ret_ty = ag->fn_ret_ty;
- bool prev_fn_var_args = ag->fn_var_args;
- ag->fn_var_args = is_var_args;
- // Use ret_ref_raw (before void_type→none mapping) to match upstream
- // AstGen.zig:4449: fn_ret_ty = if (is_inferred_error or
- // ret_ref.toIndex() != null) body_gz.addNode(.ret_type) else ret_ref.
- if (is_inferred_error || ret_ref_raw >= ZIR_REF_START_INDEX) {
- ZirInstData rtdata;
- memset(&rtdata, 0, sizeof(rtdata));
- rtdata.node = (int32_t)node - (int32_t)body_gz.decl_node_index;
- ag->fn_ret_ty = addInstruction(&body_gz, ZIR_INST_RET_TYPE, rtdata);
- } else {
- ag->fn_ret_ty = ret_ref_raw;
- }
-
- // Process function body (AstGen.zig:4461-4465).
- advanceSourceCursorToNode(ag, body_node);
- uint32_t lbrace_line = ag->source_line - decl_line;
- uint32_t lbrace_column = ag->source_column;
-
- fullBodyExpr(&body_gz, &body_gz.base, RL_NONE_VAL, body_node);
+ } // end if (body_node != 0)
ag->within_fn = prev_within_fn;
- ag->fn_block = prev_fn_block;
- ag->fn_ret_ty = prev_fn_ret_ty;
- ag->fn_var_args = prev_fn_var_args;
-
- if (ag->has_compile_errors) {
- free(ret_body);
- free(cc_body);
- return;
- }
-
- // Add implicit return at end of function body
- // (AstGen.zig:4465-4871).
- if (!endsWithNoReturn(&body_gz)) {
- ZirInstData rdata;
- rdata.un_node.operand = ZIR_REF_NONE;
- rdata.un_node.src_node
- = (int32_t)node - (int32_t)body_gz.decl_node_index;
- addInstruction(
- &body_gz, ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL, rdata);
- uint32_t body_last_tok = lastToken(tree, body_node);
- ZirInstData rdata2;
- rdata2.un_tok.operand = ZIR_REF_VOID_VALUE;
- rdata2.un_tok.src_tok = tokenIndexToRelative(&body_gz, body_last_tok);
- addInstruction(&body_gz, ZIR_INST_RET_IMPLICIT, rdata2);
- }
-
- // Read body before unstacking (AstGen.zig:12215-12218).
- const uint32_t* fn_body = gzInstructionsSlice(&body_gz);
- uint32_t fn_body_len = gzInstructionsLen(&body_gz);
- gzUnstack(&body_gz);
-
- // Create func/func_fancy instruction (AstGen.zig:4476-4494,
- // 12112-12173).
- bool need_fancy = cc_ref != ZIR_REF_NONE || is_var_args
- || noalias_bits != 0 || is_noinline;
- if (need_fancy) {
- func_ref = addFuncFancy(&decl_gz, node, body_node, decl_inst, ret_ref,
- ret_body, ret_body_len, cc_ref, cc_body, cc_body_len, fn_body,
- fn_body_len, param_insts, param_insts_len, lbrace_line,
- lbrace_column, is_var_args, is_inferred_error, is_noinline,
- noalias_bits, ret_ty_is_generic, ret_param_refs,
- ret_param_refs_len);
- } else {
- func_ref = addFunc(&decl_gz, node, body_node, decl_inst, ret_ref,
- ret_body, ret_body_len, fn_body, fn_body_len, param_insts,
- param_insts_len, lbrace_line, lbrace_column, is_inferred_error,
- ret_ty_is_generic, ret_param_refs, ret_param_refs_len);
- }
-
- // Patch ret_body break_inline to point to func instruction
- // (AstGen.zig:12199-12202).
- if (ret_body_len > 0) {
- uint32_t break_inst = ret_body[ret_body_len - 1];
- uint32_t break_payload
- = ag->inst_datas[break_inst].break_data.payload_index;
- ag->extra[break_payload + 1] = func_ref - ZIR_REF_START_INDEX;
- }
- // Patch cc_body break_inline to point to func instruction
- // (AstGen.zig:12146-12148).
- if (cc_body_len > 0) {
- uint32_t break_inst = cc_body[cc_body_len - 1];
- uint32_t break_payload
- = ag->inst_datas[break_inst].break_data.payload_index;
- ag->extra[break_payload + 1] = func_ref - ZIR_REF_START_INDEX;
- }
- free(ret_body);
- free(cc_body);
-
- // break_inline returning func to declaration (AstGen.zig:4495).
- // nodeIndexToRelative(decl_node) = node - decl_gz.decl_node_index.
- makeBreakInline(
- &decl_gz, decl_inst, func_ref, (int32_t)node - (int32_t)proto_node);
-
-fn_decl_finish:
// setDeclaration (AstGen.zig:4208-4225).
- ;
// Compute sub-block body slices.
const uint32_t* type_body
= ag->scratch_instructions + type_gz.instructions_top;
@@ -14259,7 +14479,8 @@ static void comptimeDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t* wip_decl_insts, uint32_t* decl_idx, uint32_t node) {
const Ast* tree = ag->tree;
- // makeDeclaration before advanceSourceCursorToNode (AstGen.zig:4663-4665).
+ // makeDeclaration before advanceSourceCursorToNode
+ // (AstGen.zig:4663-4665).
uint32_t decl_inst = makeDeclaration(ag, node);
wip_decl_insts[*decl_idx] = decl_inst;
(*decl_idx)++;
@@ -14375,7 +14596,8 @@ static VarDeclInfo extractVarDecl(const Ast* tree, uint32_t node) {
case AST_NODE_LOCAL_VAR_DECL: {
// extra_and_opt_node: lhs = extra_data index,
// rhs = init_node (OptionalIndex).
- // Extra data: LocalVarDecl with plain Index fields (always present).
+ // Extra data: LocalVarDecl with plain Index fields (always
+ // present).
uint32_t ei = nd.lhs;
info.type_node = tree->extra_data.arr[ei + 0];
info.align_node = tree->extra_data.arr[ei + 1];
@@ -14569,7 +14791,8 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
lib_name = li;
}
- // advanceSourceCursorToNode before makeDeclaration (AstGen.zig:4542-4546).
+ // advanceSourceCursorToNode before makeDeclaration
+ // (AstGen.zig:4542-4546).
advanceSourceCursorToNode(ag, node);
uint32_t decl_column = ag->source_column;
@@ -14720,7 +14943,8 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
} else {
init_rl.tag = RL_NONE;
}
- // nameStratExpr: check if init is container decl (AstGen.zig:4617).
+ // nameStratExpr: check if init is container decl
+ // (AstGen.zig:4617).
uint32_t init_ref;
if (!nameStratExpr(&value_gz, &value_gz.base, init_rl, vd.init_node,
0 /* parent */, &init_ref)) {
@@ -14777,8 +15001,9 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
// Unstack all sub-gzs in reverse order, matching Zig's defer ordering
// (AstGen.zig:4578/4586/4594/4602/4611). Each unstack truncates the
// shared instruction array back to where that scope started, so the
- // final type_gz unstack restores scratch_inst_len to where the caller's
- // scope was before globalVarDecl added any sub-scope instructions.
+ // final type_gz unstack restores scratch_inst_len to where the
+ // caller's scope was before globalVarDecl added any sub-scope
+ // instructions.
gzUnstack(&value_gz);
gzUnstack(&addrspace_gz);
gzUnstack(&linksection_gz);
@@ -14797,8 +15022,8 @@ static bool identImpliesMoreThanOnePossibleValue(
// Match known primitive types that have more than one possible value.
// (AstGen.zig:10729-10766)
if (src[0] == 'u' || src[0] == 'i') {
- // u8, u16, u32, u64, u128, u1, u29, usize, i8, i16, i32, i64, i128,
- // isize
+ // u8, u16, u32, u64, u128, u1, u29, usize, i8, i16, i32, i64,
+ // i128, isize
char c1 = src[1];
if (c1 >= '0' && c1 <= '9')
return true;
@@ -14922,9 +15147,11 @@ static bool nodeImpliesComptimeOnly(const Ast* tree, uint32_t node) {
}
// --- WipMembers (AstGen.zig:3989) ---
-// Tracks decl indices, field bit-flags, and per-field data during container
-// processing. All data lives in a single malloc'd array laid out as:
-// [decls (decl_count)] [field_bits (ceil)] [fields (up to field_count*max)]
+// Tracks decl indices, field bit-flags, and per-field data during
+// container processing. All data lives in a single malloc'd array laid out
+// as:
+// [decls (decl_count)] [field_bits (ceil)] [fields (up to
+// field_count*max)]
// Bodies are tracked separately in a dynamic array.
typedef struct {
@@ -15047,7 +15274,8 @@ static const uint32_t* wipMembersDeclsSlice(
return wm->payload + wm->payload_top;
}
-// Returns pointer to fields region (field_bits + field_data) and its length.
+// Returns pointer to fields region (field_bits + field_data) and its
+// length.
static const uint32_t* wipMembersFieldsSlice(
const WipMembers* wm, uint32_t* out_len) {
*out_len = wm->fields_end - wm->field_bits_start;
@@ -15178,8 +15406,8 @@ static uint32_t containerDecl(
uint32_t decl_inst;
switch (kw_tag) {
case TOKEN_KEYWORD_STRUCT: {
- // Extract layout from token before main_token (AstGen.zig:5489-5493).
- // auto=0, extern=1, packed=2.
+ // Extract layout from token before main_token
+ // (AstGen.zig:5489-5493). auto=0, extern=1, packed=2.
uint8_t layout = 0; // auto
if (main_token > 0) {
TokenizerTag prev_tag = tree->tokens.tags[main_token - 1];
@@ -15284,7 +15512,8 @@ static void setEnum(AstGenCtx* ag, uint32_t inst, uint32_t src_node,
uint32_t payload_index = ag->extra_len;
- // fields_hash (4 words): zero-filled; hash comparison skipped in tests.
+ // fields_hash (4 words): zero-filled; hash comparison skipped in
+ // tests.
ag->extra[ag->extra_len++] = 0;
ag->extra[ag->extra_len++] = 0;
ag->extra[ag->extra_len++] = 0;
@@ -15338,8 +15567,8 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t arg_node, uint8_t name_strategy) {
const Ast* tree = ag->tree;
- // --- First pass: count fields, values, decls, detect nonexhaustive ---
- // (AstGen.zig:5513-5590)
+ // --- First pass: count fields, values, decls, detect nonexhaustive
+ // --- (AstGen.zig:5513-5590)
uint32_t total_fields = 0;
uint32_t decl_count = 0;
uint32_t nonexhaustive_index = UINT32_MAX; // index into members[]
@@ -15395,7 +15624,8 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope,
if (arg_node != 0)
arg_inst = typeExpr(&block_scope, &block_scope.base, arg_node);
- // WipMembers with bits_per_field=1, max_field_size=2 (AstGen.zig:5645).
+ // WipMembers with bits_per_field=1, max_field_size=2
+ // (AstGen.zig:5645).
WipMembers wm = wipMembersInitEx(decl_count, total_fields, 1, 2);
// --- Second pass: process members (AstGen.zig:5656-5693) ---
@@ -15528,7 +15758,8 @@ static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
return reserveInstructionIndex(ag);
}
- // Build fields_start scratch area: for each field, type ref + init ref.
+ // Build fields_start scratch area: for each field, type ref + init
+ // ref.
uint32_t fields_start = ag->extra_len;
// We use extra as scratch temporarily; will finalize below.
// Actually, upstream uses astgen.scratch; we use a temporary buffer.
@@ -15719,7 +15950,8 @@ static uint32_t unionDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope,
arg_inst = typeExpr(&block_scope, &namespace.base, arg_node);
}
- // WipMembers with bits_per_field=4, max_field_size=4 (AstGen.zig:5347).
+ // WipMembers with bits_per_field=4, max_field_size=4
+ // (AstGen.zig:5347).
WipMembers wm = wipMembersInitEx(decl_count, field_count, 4, 4);
bool any_aligned_fields = false;
@@ -15795,7 +16027,8 @@ static uint32_t unionDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope,
tuple_like = false;
}
- // Still tuple-like after conversion: error (AstGen.zig:5366-5368).
+ // Still tuple-like after conversion: error
+ // (AstGen.zig:5366-5368).
if (tuple_like) {
SET_ERROR(ag);
break;
@@ -16008,7 +16241,8 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope,
block_scope.any_defer_node = UINT32_MAX;
// Handle backing_int_node for packed structs (AstGen.zig:5000-5024).
- // We store the raw body instructions and apply fixups at the final append.
+ // We store the raw body instructions and apply fixups at the final
+ // append.
uint32_t backing_int_body_raw_len = 0;
uint32_t backing_int_ref = ZIR_REF_NONE;
uint32_t* backing_int_body_raw = NULL;
@@ -16542,7 +16776,8 @@ static void rlContainerDecl(AstGenCtx* ag, RlBlock* block, uint32_t node) {
// Extract arg and members depending on variant.
// All container decls: recurse arg with type_only, members with none.
- // (The keyword type — struct/union/enum/opaque — doesn't matter for RL.)
+ // (The keyword type — struct/union/enum/opaque — doesn't matter for
+ // RL.)
uint32_t member_buf[2];
const uint32_t* members = NULL;
uint32_t members_len = 0;
diff --git a/stage0/astgen_test.zig b/stage0/astgen_test.zig
@@ -1216,7 +1216,7 @@ const corpus_files = .{
.{ "addrspace_and_linksection.zig", @embedFile("../test/behavior/addrspace_and_linksection.zig") },
.{ "align.zig", @embedFile("../test/behavior/align.zig") },
.{ "alignof.zig", @embedFile("../test/behavior/alignof.zig") },
- //.{ "asm.zig", @embedFile("../test/behavior/asm.zig") },
+ .{ "asm.zig", @embedFile("../test/behavior/asm.zig") },
//.{ "atomics.zig", @embedFile("../test/behavior/atomics.zig") },
.{ "bitcast.zig", @embedFile("../test/behavior/bitcast.zig") },
.{ "bitreverse.zig", @embedFile("../test/behavior/bitreverse.zig") },