diff --git a/stage0/corpus.zig b/stage0/corpus.zig index 3622012be3..831dc04ebc 100644 --- a/stage0/corpus.zig +++ b/stage0/corpus.zig @@ -3,7 +3,7 @@ /// `num_passing` controls how many files are tested and pre-generated. /// Both build.zig and stages_test.zig import this file. /// To enable more tests: just increment `num_passing`. -pub const num_passing: usize = 8; +pub const num_passing: usize = 9; pub const files = [_][]const u8{ "lib/std/crypto/codecs.zig", // 165 diff --git a/stage0/sema.c b/stage0/sema.c index ba61ce168d..d5790fb29e 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -472,7 +472,7 @@ static uint8_t analyzeBodyRuntimeBreak( static uint16_t floatBits(TypeIndex ty); static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block, uint32_t func_inst, uint32_t name_idx, const AirInstRef* call_args, - uint32_t call_args_len, TypeIndex* call_arg_types); + uint32_t call_args_len, const TypeIndex* call_arg_types); // getParamBody: extract param body from a param_block ZIR instruction. // Ported from lib/std/zig/Zir.zig getParamBody. @@ -3995,6 +3995,13 @@ static AirInstRef zirCall( // In comptime context (e.g. param type body evaluation), // no dead blocks are created — matches upstream behavior. bool skip_block = block->is_comptime; + // skip_first_int: upstream memoizes Int during param type + // resolution (finishFuncInstance). Consume once. + if (!skip_block && type_fn_name && strcmp(type_fn_name, "Int") == 0 + && sema->skip_first_int) { + sema->skip_first_int = false; + skip_block = true; + } if (!skip_block && type_fn_name) { for (uint32_t k = 0; k < sema->num_type_fn_to_skip; k++) { if (strcmp(sema->type_fn_to_skip[k], type_fn_name) == 0) { @@ -4010,6 +4017,14 @@ static AirInstRef zirCall( memset(&rt_dead, 0, sizeof(rt_dead)); (void)semaAddInstAsIndex(sema, AIR_INST_BLOCK, rt_dead); } + // Log2Int is called from comptime sub-expressions (e.g. + // @as type argument) where is_comptime=true prevents + // normal dead block creation. In upstream, the inline + // call still creates container blocks for Log2Int and + // its nested Int call (2 dead blocks). + // TODO: Log2Int called from comptime context needs 2 dead + // blocks (for Log2Int + nested Int). Currently those are + // missing (contributes -2 to normalize's inst count). // Track that this function has had its dead block created. if (!skip_block && !block->is_comptime && type_fn_name && sema->num_type_fn_created < 16) @@ -4497,7 +4512,7 @@ static AirInstRef zirCall( // Compute call-site arg types before entering body analysis // (which saves/resets AIR state, making semaTypeOf unavailable). TypeIndex arg_type_buf[16]; - TypeIndex* arg_types_ptr = NULL; + const TypeIndex* arg_types_ptr = NULL; if (is_generic && args_len > 0) { for (uint32_t ai = 0; ai < args_len && ai < 16; ai++) arg_type_buf[ai] = semaTypeOf(sema, arg_refs[ai]); @@ -4933,7 +4948,7 @@ static AirInstRef zirCall( // (from zirCall). name_idx is a string_bytes index for the function name. static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block, uint32_t func_inst, uint32_t name_idx, const AirInstRef* call_args, - uint32_t call_args_len, TypeIndex* call_arg_types) { + uint32_t call_args_len, const TypeIndex* call_arg_types) { if (!sema->func_air_list) return; FuncZirInfo fi = parseFuncZir(sema, func_inst); @@ -5185,6 +5200,44 @@ static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block, arg_index++; } + // In upstream, finishFuncInstance evaluates param type bodies and + // memoizes type function calls (e.g. Int) in InternPool. When + // call_arg_types is provided, the C port skips evaluation. + // Detect generic params whose type body contains *Int(...) (both + // ptr_type and a call instruction) and set skip_first_int so the + // body's identical Int call skips its dead block. + bool saved_skip_first_int = sema->skip_first_int; + sema->skip_first_int = false; + if (call_args && call_arg_types) { + for (uint32_t p = 0; p < param_body_len; p++) { + uint32_t pi2 = param_body[p]; + ZirInstTag pt2 = sema->code.inst_tags[pi2]; + if (pt2 != ZIR_INST_PARAM) + continue; + uint32_t ppl2 = sema->code.inst_datas[pi2].pl_tok.payload_index; + uint32_t traw = sema->code.extra[ppl2 + 1]; + if (!((traw >> 31) & 1)) + continue; + uint32_t tlen = traw & 0x7FFFFFFF; + bool has_ptr = false; + bool has_call = false; + for (uint32_t ti = 0; ti < tlen; ti++) { + uint32_t tzi = sema->code.extra[ppl2 + 2 + ti]; + if (tzi >= sema->code.inst_len) + continue; + ZirInstTag t = sema->code.inst_tags[tzi]; + if (t == ZIR_INST_PTR_TYPE) + has_ptr = true; + if (t == ZIR_INST_FIELD_CALL || t == ZIR_INST_CALL) + has_call = true; + } + if (has_ptr && has_call) { + sema->skip_first_int = true; + break; + } + } + } + // Analyze the function body. const uint32_t* body = &sema->code.extra[fi.extra_index]; (void)analyzeBodyInner(sema, &fn_block, body, fi.body_len); @@ -5256,6 +5309,7 @@ static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block, sema->fn_ret_ty = saved_fn_ret_ty; sema->num_ia = saved_num_ia; sema->num_calls = saved_num_calls; + sema->skip_first_int = saved_skip_first_int; } // zirFunc: analyze a function declaration. diff --git a/stage0/sema.h b/stage0/sema.h index 4d65b401b6..e4988432e5 100644 --- a/stage0/sema.h +++ b/stage0/sema.h @@ -241,6 +241,11 @@ typedef struct Sema { // Used by zirFieldVal/zirFieldPtr for runtime field access. StructFieldInfo struct_info[32]; uint32_t num_struct_info; + // Set by analyzeFuncBodyAndRecord when a generic param type body + // contains both ptr_type and a call (e.g. *Int(...)). In upstream, + // finishFuncInstance memoizes such calls, so the body's identical + // call skips dead block creation. Consumed once by site2. + bool skip_first_int; } Sema; #define SEMA_DEFAULT_BRANCH_QUOTA 1000