diff --git a/stage0/astgen.c b/stage0/astgen.c index 72a473ad3d..cc2c7c14c6 100644 --- a/stage0/astgen.c +++ b/stage0/astgen.c @@ -111,8 +111,7 @@ typedef struct { bool fn_var_args; // AstGen.zig:46 } AstGenCtx; -static void setCompileError(AstGenCtx* ag) { ag->has_compile_errors = true; } -#define SET_ERROR(ag) setCompileError(ag) +#define SET_ERROR(ag) ((ag)->has_compile_errors = true) // Set fn_block pointer on AstGenCtx. The caller is responsible for saving // and restoring the previous value before the pointed-to GenZir goes out @@ -2610,15 +2609,15 @@ static bool nameStratExpr(GenZir* gz, Scope* scope, ResultLoc rl, static bool tokenIsUnderscore(const Ast* tree, uint32_t ident_token); static uint32_t containerDecl( GenZir* gz, Scope* scope, uint32_t node, uint8_t name_strategy); -static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint8_t layout, - uint32_t backing_int_node, uint8_t name_strategy); -static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint8_t layout, - uint32_t backing_int_node); -static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint32_t arg_node, - uint8_t name_strategy); +static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint8_t layout, uint32_t backing_int_node, uint8_t name_strategy); +static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint8_t layout, uint32_t backing_int_node); +static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint32_t arg_node, uint8_t name_strategy); static uint32_t blockExprExpr( GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); @@ -2650,6 +2649,24 @@ static uint32_t identAsString(AstGenCtx* ag, uint32_t token); static uint32_t lastToken(const Ast* tree, uint32_t node); static uint32_t simpleBinOp( GenZir* gz, Scope* scope, uint32_t node, ZirInstTag tag); +static uint32_t addParam(GenZir* gz, GenZir* param_gz, ZirInstTag tag, + uint32_t abs_tok_index, uint32_t name, bool is_generic); +static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node, + uint32_t param_block, uint32_t ret_ref, const uint32_t* ret_body, + uint32_t ret_body_len, const uint32_t* body, uint32_t body_len, + const uint32_t* param_insts, uint32_t param_insts_len, + uint32_t lbrace_line, uint32_t lbrace_column, bool is_inferred_error, + bool ret_ty_is_generic, const uint32_t* ret_param_refs, + uint32_t ret_param_refs_len); +static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node, + uint32_t block_node, uint32_t param_block, uint32_t ret_ref, + const uint32_t* ret_body, uint32_t ret_body_len, uint32_t cc_ref, + const uint32_t* cc_body, uint32_t cc_body_len, const uint32_t* body, + uint32_t body_len, const uint32_t* param_insts, uint32_t param_insts_len, + uint32_t lbrace_line, uint32_t lbrace_column, bool is_var_args, + bool is_inferred_error, bool is_noinline, uint32_t noalias_bits, + bool ret_ty_is_generic, const uint32_t* ret_param_refs, + uint32_t ret_param_refs_len); // Mirrors GenZir.endsWithNoReturn (AstGen.zig:11770). static bool endsWithNoReturn(GenZir* gz) { @@ -2759,7 +2776,11 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node); #define COMPTIME_REASON_ALIGN 50 #define COMPTIME_REASON_ADDRSPACE 51 #define COMPTIME_REASON_FIELD_NAME 42 +#define COMPTIME_REASON_OPERAND_BRANCH_HINT 3 +#define COMPTIME_REASON_OPERAND_SET_RUNTIME_SAFETY 4 +#define COMPTIME_REASON_FUNCTION_RET_TY 39 #define COMPTIME_REASON_COMPTIME_KEYWORD 53 +#define COMPTIME_REASON_CALLCONV 49 #define COMPTIME_REASON_ARRAY_MUL_FACTOR 22 #define COMPTIME_REASON_COMPILE_ERROR_STRING 19 #define COMPTIME_REASON_SWITCH_ITEM 56 @@ -3577,9 +3598,11 @@ static uint32_t builtinCall( return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_FLOAT_CAST, node, result_type, operand), node); } - // @errSetCast — extended error_cast (AstGen.zig:9454-9463). - if (name_len == 10 - && memcmp(source + name_start, "errSetCast", 10) == 0) { + // @errSetCast / @errorCast — extended error_cast (AstGen.zig:9454-9463). + if ((name_len == 10 + && memcmp(source + name_start, "errSetCast", 10) == 0) + || (name_len == 9 + && memcmp(source + name_start, "errorCast", 9) == 0)) { emitDbgNode(gz, node); uint32_t result_type = rlResultTypeForCast(gz, rl, node); AstData nd = tree->nodes.datas[node]; @@ -3594,6 +3617,177 @@ static uint32_t builtinCall( gz, (uint16_t)ZIR_EXT_ERROR_CAST, payload_index); return rvalue(gz, rl, result, node); } + // @ptrFromInt — typeCast (AstGen.zig:9413). + if (name_len == 10 + && memcmp(source + name_start, "ptrFromInt", 10) == 0) { + advanceSourceCursorToMainToken(ag, gz, node); + uint32_t saved_line = ag->source_line - gz->decl_line; + uint32_t saved_col = ag->source_column; + uint32_t result_type = rlResultTypeForCast(gz, rl, node); + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + emitDbgStmt(gz, saved_line, saved_col); + return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_PTR_FROM_INT, + node, result_type, operand), node); + } + // @Vector — type construction (AstGen.zig:9675). + if (name_len == 6 && memcmp(source + name_start, "Vector", 6) == 0) { + AstData nd = tree->nodes.datas[node]; + ResultLoc u32_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_U32_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t len = comptimeExpr( + gz, scope, u32_rl, nd.lhs, COMPTIME_REASON_TYPE); + uint32_t elem_type = typeExpr(gz, scope, nd.rhs); + uint32_t result = addPlNodeBin( + gz, ZIR_INST_VECTOR_TYPE, node, len, elem_type); + return rvalue(gz, rl, result, node); + } + // @setRuntimeSafety — simpleUnOp (AstGen.zig:9392). + if (name_len == 16 + && memcmp(source + name_start, "setRuntimeSafety", 16) == 0) { + AstData nd = tree->nodes.datas[node]; + ResultLoc bool_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_BOOL_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t operand = comptimeExpr( + gz, scope, bool_rl, nd.lhs, + COMPTIME_REASON_OPERAND_SET_RUNTIME_SAFETY); + addUnNode(gz, ZIR_INST_SET_RUNTIME_SAFETY, operand, node); + return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node); + } + // @intFromError — extended UnNode (AstGen.zig:9438-9444). + if (name_len == 12 + && memcmp(source + name_start, "intFromError", 12) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + ensureExtraCapacity(ag, 2); + uint32_t payload_index = ag->extra_len; + ag->extra[ag->extra_len++] + = (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index); + ag->extra[ag->extra_len++] = operand; + uint32_t result = addExtendedPayload( + gz, (uint16_t)ZIR_EXT_INT_FROM_ERROR, payload_index); + return rvalue(gz, rl, result, node); + } + // @clz — bitBuiltin (AstGen.zig:9475). + if (name_len == 3 && memcmp(source + name_start, "clz", 3) == 0) { + advanceSourceCursorToMainToken(ag, gz, node); + uint32_t saved_line = ag->source_line - gz->decl_line; + uint32_t saved_col = ag->source_column; + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + emitDbgStmt(gz, saved_line, saved_col); + uint32_t result = addUnNode(gz, ZIR_INST_CLZ, operand, node); + return rvalue(gz, rl, result, node); + } + // @branchHint — extended UnNode (AstGen.zig:9230-9239). + if (name_len == 10 + && memcmp(source + name_start, "branchHint", 10) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t hint_ty + = addBuiltinValue(gz, node, ZIR_BUILTIN_VALUE_BRANCH_HINT); + ResultLoc hint_rl = { .tag = RL_COERCED_TY, + .data = hint_ty, .src_node = 0, .ctx = RI_CTX_NONE }; + uint32_t hint_val = comptimeExpr( + gz, scope, hint_rl, nd.lhs, + COMPTIME_REASON_OPERAND_BRANCH_HINT); + ensureExtraCapacity(ag, 2); + uint32_t payload_index = ag->extra_len; + ag->extra[ag->extra_len++] + = (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index); + ag->extra[ag->extra_len++] = hint_val; + addExtendedPayload( + gz, (uint16_t)ZIR_EXT_BRANCH_HINT, payload_index); + return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node); + } + // @bitSizeOf — simpleUnOpType (AstGen.zig:9382). + if (name_len == 9 + && memcmp(source + name_start, "bitSizeOf", 9) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = typeExpr(gz, scope, nd.lhs); + uint32_t result + = addUnNode(gz, ZIR_INST_BIT_SIZE_OF, operand, node); + return rvalue(gz, rl, result, node); + } + // @fieldParentPtr — extended FieldParentPtr (AstGen.zig:9620-9628). + if (name_len == 14 + && memcmp(source + name_start, "fieldParentPtr", 14) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t parent_ptr_type = rlResultTypeForCast(gz, rl, node); + ResultLoc field_name_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_SLICE_CONST_U8_TYPE, + .src_node = 0, .ctx = RI_CTX_NONE }; + uint32_t field_name = comptimeExpr( + gz, scope, field_name_rl, nd.lhs, COMPTIME_REASON_FIELD_NAME); + uint32_t field_ptr = expr(gz, scope, nd.rhs); + ensureExtraCapacity(ag, 4); + uint32_t payload_index = ag->extra_len; + ag->extra[ag->extra_len++] + = (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index); + ag->extra[ag->extra_len++] = parent_ptr_type; + ag->extra[ag->extra_len++] = field_name; + ag->extra[ag->extra_len++] = field_ptr; + uint32_t result = addExtendedPayloadSmall( + gz, (uint16_t)ZIR_EXT_FIELD_PARENT_PTR, 0, payload_index); + return rvalue(gz, rl, result, node); + } + // @splat — AstGen.zig:9529-9537. + if (name_len == 5 + && memcmp(source + name_start, "splat", 5) == 0) { + uint32_t result_type = rlResultTypeForCast(gz, rl, node); + uint32_t elem_type + = addUnNode(gz, ZIR_INST_SPLAT_OP_RESULT_TY, result_type, node); + ResultLoc elem_rl + = { .tag = RL_TY, .data = elem_type, .src_node = 0, + .ctx = RI_CTX_NONE }; + AstData nd = tree->nodes.datas[node]; + uint32_t scalar = exprRl(gz, scope, elem_rl, nd.lhs); + uint32_t result = addPlNodeBin( + gz, ZIR_INST_SPLAT, node, result_type, scalar); + return rvalue(gz, rl, result, node); + } + // @offsetOf — AstGen.zig:9962-9978. + if (name_len == 8 + && memcmp(source + name_start, "offsetOf", 8) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t type_inst = typeExpr(gz, scope, nd.lhs); + ResultLoc field_name_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_SLICE_CONST_U8_TYPE, + .src_node = 0, .ctx = RI_CTX_NONE }; + uint32_t field_name = comptimeExpr( + gz, scope, field_name_rl, nd.rhs, COMPTIME_REASON_FIELD_NAME); + uint32_t result = addPlNodeBin( + gz, ZIR_INST_OFFSET_OF, node, type_inst, field_name); + return rvalue(gz, rl, result, node); + } + // @inComptime — AstGen.zig:9420-9424. + if (name_len == 10 + && memcmp(source + name_start, "inComptime", 10) == 0) { + if (gz->is_comptime) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + uint32_t result + = addNodeExtended(gz, (uint16_t)ZIR_EXT_IN_COMPTIME, node); + return rvalue(gz, rl, result, node); + } + // @errorFromInt — AstGen.zig:9446-9452. + if (name_len == 12 + && memcmp(source + name_start, "errorFromInt", 12) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + ensureExtraCapacity(ag, 2); + uint32_t payload_index = ag->extra_len; + ag->extra[ag->extra_len++] + = (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index); + ag->extra[ag->extra_len++] = operand; + uint32_t result = addExtendedPayloadSmall( + gz, (uint16_t)ZIR_EXT_ERROR_FROM_INT, 0, payload_index); + return rvalue(gz, rl, result, node); + } // clang-format on // TODO: handle other builtins. @@ -4297,6 +4491,370 @@ static uint32_t arrayTypeExpr(GenZir* gz, Scope* scope, uint32_t node) { return addPlNodeBin(gz, ZIR_INST_ARRAY_TYPE, node, len, elem_type); } +// --- arrayTypeSentinel (AstGen.zig:3965) --- +// Handles [N:sentinel]T array type expressions. + +static uint32_t arrayTypeSentinelExpr( + GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) { + AstGenCtx* ag = gz->astgen; + const Ast* tree = ag->tree; + AstData nd = tree->nodes.datas[node]; + + // data.lhs = length expression node, data.rhs = extra index. + uint32_t len_node = nd.lhs; + uint32_t extra_idx = nd.rhs; + uint32_t sentinel_node = tree->extra_data.arr[extra_idx]; // sentinel + uint32_t elem_type_node = tree->extra_data.arr[extra_idx + 1]; // elem_type + + // Check for `_` identifier → compile error (AstGen.zig:3972-3975). + if (tree->nodes.tags[len_node] == AST_NODE_IDENTIFIER + && isUnderscoreIdent(tree, len_node)) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + + // Length expression (AstGen.zig:3977). + ResultLoc usize_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_USIZE_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t len = reachableExprComptime( + gz, scope, usize_rl, len_node, node, COMPTIME_REASON_ARRAY_LENGTH); + // Element type (AstGen.zig:3978). + uint32_t elem_type = typeExpr(gz, scope, elem_type_node); + // Sentinel expression (AstGen.zig:3979). + ResultLoc sentinel_rl = { .tag = RL_COERCED_TY, + .data = elem_type, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t sentinel = reachableExprComptime(gz, scope, sentinel_rl, + sentinel_node, node, COMPTIME_REASON_ARRAY_SENTINEL); + + uint32_t result = addPlNodeTriple( + gz, ZIR_INST_ARRAY_TYPE_SENTINEL, node, len, elem_type, sentinel); + return rvalue(gz, rl, result, node); +} + +// --- fnProtoExprInner (AstGen.zig:1313) --- +// Handles function type expressions: fn(params) RetType. +// Used for fn_proto_simple/fn_proto_one/fn_proto_multi/fn_proto as +// expressions (not declarations), and for extern fn_proto in fnDecl. + +static uint32_t fnProtoExprInner( + GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool implicit_ccc) { + AstGenCtx* ag = gz->astgen; + const Ast* tree = ag->tree; + AstNodeTag proto_tag = tree->nodes.tags[node]; + AstData proto_data = tree->nodes.datas[node]; + + // Extract param nodes and callconv_expr from the proto node + // (same logic as fnDecl, AstGen.zig:4067-4136). + uint32_t param_nodes_buf[1]; + const uint32_t* param_nodes = NULL; + uint32_t params_len = 0; + uint32_t callconv_expr_node = 0; + uint32_t return_type_rhs = proto_data.rhs; + + if (proto_tag == AST_NODE_FN_PROTO_SIMPLE) { + if (proto_data.lhs != 0) { + param_nodes_buf[0] = proto_data.lhs; + param_nodes = param_nodes_buf; + params_len = 1; + } + } else if (proto_tag == AST_NODE_FN_PROTO_ONE) { + uint32_t extra_idx = proto_data.lhs; + uint32_t param = tree->extra_data.arr[extra_idx]; + if (param != 0) { + param_nodes_buf[0] = param; + param_nodes = param_nodes_buf; + params_len = 1; + } + callconv_expr_node = tree->extra_data.arr[extra_idx + 4]; + } else if (proto_tag == AST_NODE_FN_PROTO_MULTI) { + uint32_t extra_idx = proto_data.lhs; + uint32_t range_start = tree->extra_data.arr[extra_idx]; + uint32_t range_end = tree->extra_data.arr[extra_idx + 1]; + param_nodes = tree->extra_data.arr + range_start; + params_len = range_end - range_start; + } else if (proto_tag == AST_NODE_FN_PROTO) { + uint32_t extra_idx = proto_data.lhs; + uint32_t pstart = tree->extra_data.arr[extra_idx]; + uint32_t pend = tree->extra_data.arr[extra_idx + 1]; + param_nodes = tree->extra_data.arr + pstart; + params_len = pend - pstart; + callconv_expr_node = tree->extra_data.arr[extra_idx + 5]; + } + // Normalize OPT() sentinel for callconv_expr (parser uses OPT()). + if (callconv_expr_node == UINT32_MAX) + callconv_expr_node = 0; + + GenZir block_scope = makeSubBlock(gz, scope); + uint32_t block_inst = makeBlockInst(ag, ZIR_INST_BLOCK_INLINE, gz, node); + + // Parameter iteration (AstGen.zig:1329-1384). + // Token-based iterator matching Ast.zig:2680-2768. + uint32_t noalias_bits = 0; + bool is_var_args = false; + + uint32_t fn_token = tree->nodes.main_tokens[node]; + uint32_t lparen = fn_token + 1; + uint32_t iter_tok_i = lparen + 1; + bool iter_tok_flag = true; + uint32_t iter_param_i = 0; + uint32_t param_type_i = 0; + + ResultLoc coerced_type_ri = { .tag = RL_COERCED_TY, + .data = ZIR_REF_TYPE_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + + 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) { + if (iter_param_i >= params_len) + break; + param_type_node = param_nodes[iter_param_i]; + 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; + } + iter_param_i++; + iter_tok_i = lastToken(tree, param_type_node) + 1; + if (tree->tokens.tags[iter_tok_i] == TOKEN_COMMA) + iter_tok_i++; + iter_tok_flag = true; + } else { + if (tree->tokens.tags[iter_tok_i] == TOKEN_COMMA) + iter_tok_i++; + if (tree->tokens.tags[iter_tok_i] == TOKEN_R_PAREN) + break; + while (tree->tokens.tags[iter_tok_i] == TOKEN_DOC_COMMENT) + iter_tok_i++; + if (tree->tokens.tags[iter_tok_i] == TOKEN_ELLIPSIS3) { + is_var_args = true; + break; + } + 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++; + } + 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; + } + if (tree->tokens.tags[iter_tok_i] == TOKEN_KEYWORD_ANYTYPE) { + is_anytype = true; + iter_tok_i++; + } else { + iter_tok_flag = false; + continue; + } + } + + // Determine noalias/comptime (AstGen.zig:1334-1342). + if (comptime_noalias_token != 0 + && tree->tokens.tags[comptime_noalias_token] + == TOKEN_KEYWORD_NOALIAS) { + 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; + } + + // Param name (AstGen.zig:1352-1357). + uint32_t param_name_str = 0; // empty + if (name_token != 0) { + uint32_t name_start = tree->tokens.starts[name_token]; + char nch = tree->source[name_start]; + if (nch == '_') { + uint32_t next_start = tree->tokens.starts[name_token + 1]; + if (next_start != name_start + 1) + param_name_str = identAsString(ag, name_token); + } else { + param_name_str = identAsString(ag, name_token); + } + } + + if (is_anytype) { + // anytype param (AstGen.zig:1359-1366). + 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; + addStrTok( + &block_scope, anytype_tag, param_name_str, anytype_name_token); + } else { + // Typed param (AstGen.zig:1367-1381). + GenZir param_gz = makeSubBlock(&block_scope, scope); + param_gz.is_comptime = true; + uint32_t param_type_ref = fullBodyExpr( + ¶m_gz, scope, coerced_type_ri, param_type_node); + if (ag->has_compile_errors) { + gzUnstack(¶m_gz); + gzUnstack(&block_scope); + return ZIR_REF_VOID_VALUE; + } + // break_inline target is the param inst we're about to create + // (AstGen.zig:1373-1374). + 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); + uint32_t name_tok_for_src = name_token != 0 + ? name_token + : tree->nodes.main_tokens[param_type_node]; + ZirInstTag param_tag + = is_comptime_param ? ZIR_INST_PARAM_COMPTIME : ZIR_INST_PARAM; + // prev_param_insts = &.{}, is_generic = false for fn proto exprs + // (AstGen.zig:1377-1380). + uint32_t param_inst = addParam(&block_scope, ¶m_gz, param_tag, + name_tok_for_src, param_name_str, false); + (void)param_inst_expected; + (void)param_inst; + } + param_type_i++; + } + + // Calling convention (AstGen.zig:1386-1397). + uint32_t cc_ref = ZIR_REF_NONE; + if (callconv_expr_node != 0) { + uint32_t cc_ty = addBuiltinValue(&block_scope, 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 = comptimeExpr(&block_scope, scope, cc_ri, callconv_expr_node, + COMPTIME_REASON_CALLCONV); + } else if (implicit_ccc) { + cc_ref = addBuiltinValue( + &block_scope, node, ZIR_BUILTIN_VALUE_CALLING_CONVENTION_C); + } + + // Return type (AstGen.zig:1399-1400). + uint32_t ret_ty_node = return_type_rhs; + uint32_t ret_ty = comptimeExpr(&block_scope, scope, coerced_type_ri, + ret_ty_node, COMPTIME_REASON_FUNCTION_RET_TY); + if (ag->has_compile_errors) { + gzUnstack(&block_scope); + return ZIR_REF_VOID_VALUE; + } + // Map void_type → .none (AstGen.zig:12054). + if (ret_ty == ZIR_REF_VOID_TYPE) + ret_ty = ZIR_REF_NONE; + + // addFunc with no body (AstGen.zig:1402-1422). + uint32_t result; + bool need_fancy + = cc_ref != ZIR_REF_NONE || is_var_args || noalias_bits != 0; + if (need_fancy) { + result = addFuncFancy(&block_scope, node, 0, block_inst, ret_ty, NULL, + 0, cc_ref, NULL, 0, NULL, 0, NULL, 0, 0, 0, is_var_args, false, + false, noalias_bits, false, NULL, 0); + } else { + result = addFunc(&block_scope, node, 0, block_inst, ret_ty, NULL, 0, + NULL, 0, NULL, 0, 0, 0, false, false, NULL, 0); + } + + // break_inline + setBlockBody + append (AstGen.zig:1424-1426). + makeBreakInline(&block_scope, block_inst, result, AST_NODE_OFFSET_NONE); + setBlockBody(ag, &block_scope, block_inst); + gzAppendInstruction(gz, block_inst); + + return rvalue(gz, rl, block_inst + ZIR_REF_START_INDEX, node); +} + +// --- fnProtoExpr (AstGen.zig:1271) --- +// Wrapper that validates fn_proto used as expression (no name, no align, etc.) +// then calls fnProtoExprInner. + +static uint32_t fnProtoExpr( + GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) { + AstGenCtx* ag = gz->astgen; + const Ast* tree = ag->tree; + AstNodeTag proto_tag = tree->nodes.tags[node]; + AstData proto_data = tree->nodes.datas[node]; + + // Validate: no name token (AstGen.zig:1281-1283). + // fn_proto_simple main_token is 'fn', next token would be name or '('. + // If there's a name, main_token+1 is an identifier before '('. + // For function type expressions, fn keyword is followed by '('. + // name_token check: upstream checks fn_proto.name_token which is + // derived from AST node. For fn_proto_simple, name_token is null. + // We just skip this validation since the parser already handles it. + + // Validate: no align/addrspace/section (AstGen.zig:1285-1295). + // These are only present in fn_proto_one and fn_proto. + if (proto_tag == AST_NODE_FN_PROTO_ONE) { + uint32_t extra_idx = proto_data.lhs; + uint32_t align_expr = tree->extra_data.arr[extra_idx + 1]; + uint32_t addrspace_expr = tree->extra_data.arr[extra_idx + 2]; + uint32_t section_expr = tree->extra_data.arr[extra_idx + 3]; + if (align_expr != UINT32_MAX && align_expr != 0) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + if (addrspace_expr != UINT32_MAX && addrspace_expr != 0) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + if (section_expr != UINT32_MAX && section_expr != 0) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + } else if (proto_tag == AST_NODE_FN_PROTO) { + uint32_t extra_idx = proto_data.lhs; + uint32_t align_expr = tree->extra_data.arr[extra_idx + 2]; + uint32_t addrspace_expr = tree->extra_data.arr[extra_idx + 3]; + uint32_t section_expr = tree->extra_data.arr[extra_idx + 4]; + if (align_expr != UINT32_MAX && align_expr != 0) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + if (addrspace_expr != UINT32_MAX && addrspace_expr != 0) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + if (section_expr != UINT32_MAX && section_expr != 0) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + } + + // Validate: no inferred error set (AstGen.zig:1297-1302). + uint32_t return_type_node = proto_data.rhs; + uint32_t maybe_bang = firstToken(tree, return_type_node) - 1; + if (tree->tokens.tags[maybe_bang] == TOKEN_BANG) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + + return fnProtoExprInner(gz, scope, rl, node, false); +} + // --- arrayInitExpr (AstGen.zig:1431) --- // Handles typed array init: [_]T{...}, [_:s]T{...}, and [N]T{...}. @@ -5468,6 +6026,15 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) { // array_type (AstGen.zig:940). case AST_NODE_ARRAY_TYPE: return rvalue(gz, rl, arrayTypeExpr(gz, scope, node), node); + // array_type_sentinel (AstGen.zig:3965). + case AST_NODE_ARRAY_TYPE_SENTINEL: + return arrayTypeSentinelExpr(gz, scope, rl, node); + // fn_proto as expression (AstGen.zig:1271-1311). + case AST_NODE_FN_PROTO_SIMPLE: + case AST_NODE_FN_PROTO_MULTI: + case AST_NODE_FN_PROTO_ONE: + case AST_NODE_FN_PROTO: + return fnProtoExpr(gz, scope, rl, node); // array_init variants (AstGen.zig:836-856). case AST_NODE_ARRAY_INIT: case AST_NODE_ARRAY_INIT_COMMA: @@ -11364,11 +11931,19 @@ static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node, uint32_t ret_param_refs_len) { AstGenCtx* ag = gz->astgen; const Ast* tree = ag->tree; - uint32_t rbrace_tok = lastToken(tree, block_node); - uint32_t rbrace_start = tree->tokens.starts[rbrace_tok]; - advanceSourceCursor(ag, rbrace_start); - uint32_t rbrace_line = ag->source_line - gz->decl_line; - uint32_t rbrace_column = ag->source_column; + + // SrcLocs only emitted when there's a body (AstGen.zig:12081-12108). + uint32_t src_locs_len = 0; + uint32_t rbrace_line = 0; + uint32_t rbrace_column = 0; + if (body_len > 0) { + uint32_t rbrace_tok = lastToken(tree, block_node); + uint32_t rbrace_start = tree->tokens.starts[rbrace_tok]; + advanceSourceCursor(ag, rbrace_start); + rbrace_line = ag->source_line - gz->decl_line; + rbrace_column = ag->source_column; + src_locs_len = 7; + } // Build Func payload (Zir.Inst.Func: ret_ty, param_block, body_len). // (AstGen.zig:12182-12194) @@ -11388,7 +11963,8 @@ static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node, uint32_t fixup_body_len = countBodyLenAfterFixupsExtraRefs( ag, body, body_len, param_insts, param_insts_len); - ensureExtraCapacity(ag, 3 + ret_ty_packed_len + fixup_body_len + 7); + ensureExtraCapacity( + ag, 3 + ret_ty_packed_len + fixup_body_len + src_locs_len); uint32_t payload_index = ag->extra_len; ag->extra[ag->extra_len++] = ret_ty_packed; // Func.ret_ty ag->extra[ag->extra_len++] = param_block; // Func.param_block @@ -11408,16 +11984,18 @@ static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node, appendBodyWithFixupsExtraRefs( ag, body, body_len, param_insts, param_insts_len); - // SrcLocs (AstGen.zig:12098-12106). - uint32_t columns = (lbrace_column & 0xFFFFu) | (rbrace_column << 16); - ag->extra[ag->extra_len++] = lbrace_line; - ag->extra[ag->extra_len++] = rbrace_line; - ag->extra[ag->extra_len++] = columns; - // proto_hash (4 words): zero for now. - ag->extra[ag->extra_len++] = 0; - ag->extra[ag->extra_len++] = 0; - ag->extra[ag->extra_len++] = 0; - ag->extra[ag->extra_len++] = 0; + // SrcLocs + proto_hash only when body present (AstGen.zig:12081-12108). + if (body_len > 0) { + uint32_t columns = (lbrace_column & 0xFFFFu) | (rbrace_column << 16); + ag->extra[ag->extra_len++] = lbrace_line; + ag->extra[ag->extra_len++] = rbrace_line; + ag->extra[ag->extra_len++] = columns; + // proto_hash (4 words): zero for now. + ag->extra[ag->extra_len++] = 0; + ag->extra[ag->extra_len++] = 0; + ag->extra[ag->extra_len++] = 0; + ag->extra[ag->extra_len++] = 0; + } // Emit the func instruction (AstGen.zig:12220-12226). ZirInstTag tag @@ -11443,11 +12021,19 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node, uint32_t ret_param_refs_len) { AstGenCtx* ag = gz->astgen; const Ast* tree = ag->tree; - uint32_t rbrace_tok = lastToken(tree, block_node); - uint32_t rbrace_start = tree->tokens.starts[rbrace_tok]; - advanceSourceCursor(ag, rbrace_start); - uint32_t rbrace_line = ag->source_line - gz->decl_line; - uint32_t rbrace_column = ag->source_column; + + // SrcLocs only emitted when there's a body (AstGen.zig:12081-12108). + uint32_t src_locs_len = 0; + uint32_t rbrace_line = 0; + uint32_t rbrace_column = 0; + if (body_len > 0) { + uint32_t rbrace_tok = lastToken(tree, block_node); + uint32_t rbrace_start = tree->tokens.starts[rbrace_tok]; + advanceSourceCursor(ag, rbrace_start); + rbrace_line = ag->source_line - gz->decl_line; + rbrace_column = ag->source_column; + src_locs_len = 7; + } uint32_t fixup_body_len = countBodyLenAfterFixupsExtraRefs( ag, body, body_len, param_insts, param_insts_len); @@ -11472,7 +12058,7 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node, // FuncFancy has 3 fields: param_block, body_len, bits. uint32_t total_extra = 3 + cc_extra_len + ret_extra_len - + ((noalias_bits != 0) ? 1u : 0u) + fixup_body_len + 7; + + ((noalias_bits != 0) ? 1u : 0u) + fixup_body_len + src_locs_len; ensureExtraCapacity(ag, total_extra); // FuncFancy payload (Zir.zig:2589-2610). @@ -11530,16 +12116,18 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node, appendBodyWithFixupsExtraRefs( ag, body, body_len, param_insts, param_insts_len); - // SrcLocs (AstGen.zig:12098-12106). - uint32_t columns = (lbrace_column & 0xFFFFu) | (rbrace_column << 16); - ag->extra[ag->extra_len++] = lbrace_line; - ag->extra[ag->extra_len++] = rbrace_line; - ag->extra[ag->extra_len++] = columns; - // proto_hash (4 words): zero for now. - ag->extra[ag->extra_len++] = 0; - ag->extra[ag->extra_len++] = 0; - ag->extra[ag->extra_len++] = 0; - ag->extra[ag->extra_len++] = 0; + // SrcLocs + proto_hash only when body present (AstGen.zig:12081-12108). + if (body_len > 0) { + uint32_t columns = (lbrace_column & 0xFFFFu) | (rbrace_column << 16); + ag->extra[ag->extra_len++] = lbrace_line; + ag->extra[ag->extra_len++] = rbrace_line; + ag->extra[ag->extra_len++] = columns; + // proto_hash (4 words): zero for now. + ag->extra[ag->extra_len++] = 0; + ag->extra[ag->extra_len++] = 0; + ag->extra[ag->extra_len++] = 0; + ag->extra[ag->extra_len++] = 0; + } // Emit the func_fancy instruction (AstGen.zig:12220-12226). ZirInstData data; @@ -11718,6 +12306,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, // (Ast.zig:2003-2025, AstGen.zig:4102-4106, 4240-4247). bool is_pub = false; bool is_export = false; + bool is_extern = false; bool is_noinline = false; bool has_inline_keyword = false; for (uint32_t i = fn_token; i > 0;) { @@ -11727,6 +12316,8 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, is_pub = true; else if (ttag == TOKEN_KEYWORD_EXPORT) is_export = true; + else if (ttag == TOKEN_KEYWORD_EXTERN) + is_extern = true; else if (ttag == TOKEN_KEYWORD_NOINLINE) is_noinline = true; else if (ttag == TOKEN_KEYWORD_INLINE) @@ -12121,21 +12712,54 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, gzUnstack(&cc_gz); // --- Body handling --- - // For fn_proto* (no body): extern function, emit type only + // 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 emits fnProtoExprInner; we SET_ERROR for now as - // fnProtoExprInner is not yet ported. - // TODO: implement fnProtoExprInner for extern fn support. - SET_ERROR(ag); + // 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; + 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, + 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); + } + // Patch ret_body/cc_body break_inlines (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; + } + 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); - gzUnstack(&decl_gz); - ag->within_fn = prev_within_fn; - return; + // break_inline from decl_gz to decl_inst (AstGen.zig:4163). + makeBreakInline(&decl_gz, decl_inst, func_ref, + (int32_t)node - (int32_t)proto_node); + goto fn_decl_finish; } // --- Body (AstGen.zig:4415-4424) --- @@ -12264,10 +12888,14 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, makeBreakInline( &decl_gz, decl_inst, func_ref, (int32_t)node - (int32_t)proto_node); +fn_decl_finish: // setDeclaration (AstGen.zig:4208-4225). - // Linkage: export > normal (AstGen.zig:4217). + // Linkage: extern > export > normal (AstGen.zig:4217). + ; DeclFlagsId decl_id; - if (is_export) + if (is_extern) + decl_id = is_pub ? DECL_ID_PUB_EXTERN_CONST : DECL_ID_EXTERN_CONST; + else if (is_export) decl_id = is_pub ? DECL_ID_PUB_EXPORT_CONST : DECL_ID_EXPORT_CONST; else decl_id = is_pub ? DECL_ID_PUB_CONST_SIMPLE : DECL_ID_CONST_SIMPLE; @@ -13151,21 +13779,20 @@ static uint32_t containerDecl( else if (prev_tag == TOKEN_KEYWORD_EXTERN) layout = 1; } - decl_inst = structDeclInner(ag, gz, node, members, members_len, layout, - arg_node, name_strategy); + decl_inst = structDeclInner(ag, gz, scope, node, members, members_len, + layout, arg_node, name_strategy); break; } case TOKEN_KEYWORD_ENUM: - decl_inst = enumDeclInner( - ag, gz, node, members, members_len, arg_node, name_strategy); + decl_inst = enumDeclInner(ag, gz, scope, node, members, members_len, + arg_node, name_strategy); break; default: // union/opaque: fall back to struct for now. decl_inst = structDeclInner( - ag, gz, node, members, members_len, 0, 0, name_strategy); + ag, gz, scope, node, members, members_len, 0, 0, name_strategy); break; } - (void)scope; ag->fn_block = prev_fn_block; return decl_inst + ZIR_REF_START_INDEX; @@ -13269,9 +13896,9 @@ static bool tokenIsUnderscore(const Ast* tree, uint32_t ident_token) { // Handles enum container declarations. // arg_node: the tag type expression node (e.g. u8 in enum(u8)), 0 if none. -static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint32_t arg_node, - uint8_t name_strategy) { +static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint32_t arg_node, uint8_t name_strategy) { const Ast* tree = ag->tree; // --- First pass: count fields, values, decls, detect nonexhaustive --- @@ -13307,8 +13934,7 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, // Create namespace scope (AstGen.zig:5618-5623). ScopeNamespace namespace; - scopeNamespaceInit( - &namespace, &gz->base, node, decl_inst, gz, ag->within_fn); + scopeNamespaceInit(&namespace, scope, node, decl_inst, gz, ag->within_fn); advanceSourceCursorToNode(ag, node); @@ -13449,9 +14075,10 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, // --- tupleDecl (AstGen.zig:5192) --- -static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint8_t layout, - uint32_t backing_int_node) { +static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint8_t layout, uint32_t backing_int_node) { + (void)scope; const Ast* tree = ag->tree; // layout must be auto for tuples (AstGen.zig:5204-5207). @@ -13614,9 +14241,9 @@ static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, uint32_t node, // --- structDeclInner (AstGen.zig:4926) --- -static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint8_t layout, - uint32_t backing_int_node, uint8_t name_strategy) { +static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint8_t layout, uint32_t backing_int_node, uint8_t name_strategy) { const Ast* tree = ag->tree; // Tuple detection (AstGen.zig:4939-4950). @@ -13638,8 +14265,8 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, SET_ERROR(ag); return reserveInstructionIndex(ag); } - return tupleDecl( - ag, gz, node, members, members_len, layout, backing_int_node); + return tupleDecl(ag, gz, scope, node, members, members_len, layout, + backing_int_node); } } @@ -13660,8 +14287,7 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, // Create namespace scope (AstGen.zig:4973-4979). ScopeNamespace namespace; - scopeNamespaceInit( - &namespace, &gz->base, node, decl_inst, gz, ag->within_fn); + scopeNamespaceInit(&namespace, scope, node, decl_inst, gz, ag->within_fn); advanceSourceCursorToNode(ag, node); @@ -15246,8 +15872,8 @@ Zir astGen(const Ast* ast) { const uint32_t* members = ast->extra_data.arr + members_start; uint32_t members_len = members_end - members_start; - structDeclInner( - &ag, &gen_scope, 0, members, members_len, 0, 0, 0 /* parent */); + structDeclInner(&ag, &gen_scope, &gen_scope.base, 0, members, members_len, + 0, 0, 0 /* parent */); // Write imports list (AstGen.zig:227-244). writeImports(&ag); diff --git a/stage0/astgen_test.zig b/stage0/astgen_test.zig index c1ea2154fd..a81274599a 100644 --- a/stage0/astgen_test.zig +++ b/stage0/astgen_test.zig @@ -297,11 +297,7 @@ fn expectEqualZir(gpa: Allocator, ref: Zir, got: c.Zir) !void { const got_tag: u8 = @intCast(got.inst_tags[i]); if (ref_tag != got_tag) { std.debug.print("first divergence at [{d}]: ref_tag={d} got_tag={d}\n", .{ i, ref_tag, got_tag }); - // Show ref instruction data for this position. - const rd = ref_datas[i]; - std.debug.print(" ref pl_node: src_node={d} payload={d}\n", .{ - rd.pl_node.src_node, rd.pl_node.payload_index, - }); + // Data printing skipped to avoid tagged union safety panics. // Scan for nearest declaration. var j: usize = i; while (j > 0) {