From 3f38bab2cda757edfb993c873af6c78543c9c98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Sun, 15 Feb 2026 18:23:33 +0000 Subject: [PATCH] astgen: port inline assembly support and fix extern fn handling Co-Authored-By: Claude Opus 4.6 --- stage0/astgen.c | 1097 ++++++++++++++++++++++++---------------- stage0/astgen_test.zig | 2 +- 2 files changed, 667 insertions(+), 432 deletions(-) diff --git a/stage0/astgen.c b/stage0/astgen.c index df5ebcd155..1f56954054 100644 --- 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; + // 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 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 }; + // --- 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; - 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; + // 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; - 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; - } - // Check for anytype keyword. - if (tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_ANYTYPE) { - is_anytype = true; - iter_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 { - // Not an anytype param; switch to param-array mode. - iter_tok_flag = false; - continue; + // 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; + } } - } - // 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 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); } - } 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 { + // 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; + } + + // 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++; } - // 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); - + // --- 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; - - // 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; + // 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; - // 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; + 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)); } - param_type_i++; - } + gzUnstack(&ret_gz); - // --- 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; + // 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; - 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); - - // 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; - - // --- 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). + // --- 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); } - } 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); + 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)); } - // Emit func instruction with no body (AstGen.zig:1402-1422). - bool need_fancy - = cc_ref != ZIR_REF_NONE || is_var_args || noalias_bits != 0; + 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; + } + + // 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; + + 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). + 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 index f50fd59c9d..9bf4ae8135 100644 --- 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") },