diff --git a/astgen.c b/astgen.c index 9b74a1ba8f..96aa521943 100644 --- a/astgen.c +++ b/astgen.c @@ -2339,7 +2339,7 @@ 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); static uint32_t forExpr( - GenZir* gz, Scope* scope, uint32_t node, bool is_statement); + GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_statement); static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op, ZirInstTag unwrap_code_op); @@ -4907,7 +4907,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) { // for (AstGen.zig:1043-1060). case AST_NODE_FOR_SIMPLE: case AST_NODE_FOR: - return rvalue(gz, rl, forExpr(gz, scope, node, false), node); + return forExpr(gz, scope, rlBr(rl), node, false); // Merge error sets (AstGen.zig:788-797). case AST_NODE_MERGE_ERROR_SETS: { uint32_t lhs = typeExpr(gz, scope, nd.lhs); @@ -5802,7 +5802,7 @@ static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) { #define FOR_MAX_INPUTS 16 static uint32_t forExpr( - GenZir* gz, Scope* scope, uint32_t node, bool is_statement) { + GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_statement) { AstGenCtx* ag = gz->astgen; const Ast* tree = ag->tree; AstData nd = tree->nodes.datas[node]; @@ -5813,12 +5813,29 @@ static uint32_t forExpr( bool is_inline = (main_token > 0 && tree->tokens.tags[main_token - 1] == TOKEN_KEYWORD_INLINE); + // Compute label_token (AstGen.zig fullForComponents:2341-2348). + uint32_t label_token = UINT32_MAX; + { + uint32_t tok_i = main_token; + if (is_inline) + tok_i = main_token - 1; + if (tok_i >= 2 && tree->tokens.tags[tok_i - 2] == TOKEN_IDENTIFIER + && tree->tokens.tags[tok_i - 1] == TOKEN_COLON) + label_token = tok_i - 2; + } + + // Compute break_rl from rl (AstGen.zig:6833-6845). + bool need_rl = nodesNeedRlContains(ag, node); + ResultLoc break_rl = breakResultInfo(gz, rl, node, need_rl); + bool need_result_rvalue = (break_rl.tag != rl.tag); + // Extract input nodes and body/else nodes. // FOR_SIMPLE: lhs = input node, rhs = body (Ast.zig:1960-1968). // FOR: lhs = extra_data index, rhs = packed AstFor (Ast.zig:1970-1981). uint32_t input_nodes[FOR_MAX_INPUTS]; uint32_t num_inputs; uint32_t body_node; + uint32_t else_node = 0; if (node_tag == AST_NODE_FOR_SIMPLE) { input_nodes[0] = nd.lhs; num_inputs = 1; @@ -5835,6 +5852,8 @@ static uint32_t forExpr( for (uint32_t i = 0; i < num_inputs; i++) input_nodes[i] = tree->extra_data.arr[extra_idx + i]; body_node = tree->extra_data.arr[extra_idx + num_inputs]; + if (for_data.has_else) + else_node = tree->extra_data.arr[extra_idx + num_inputs + 1]; } // Per-input arrays (AstGen.zig:6858-6862). @@ -5854,6 +5873,8 @@ static uint32_t forExpr( && tree->tokens.tags[last_cond_tok + 1] == TOKEN_COMMA); uint32_t payload_token = last_cond_tok + 3 + (has_comma ? 1 : 0); + bool any_len_checks = false; + // Process each input (AstGen.zig:6878-6925). uint32_t capture_token = payload_token; for (uint32_t i = 0; i < num_inputs; i++) { @@ -5862,19 +5883,38 @@ static uint32_t forExpr( bool capture_is_ref = (tree->tokens.tags[capture_token] == TOKEN_ASTERISK); uint32_t ident_tok = capture_token + (capture_is_ref ? 1u : 0u); + bool is_discard = tokenIsUnderscore(tree, ident_tok); capture_token = ident_tok + 2; // skip ident + comma/pipe + // Diagnostic: pointer modifier invalid on discard + // (AstGen.zig:6885-6887). + if (is_discard && capture_is_ref) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + emitDbgNode(gz, input); if (tree->nodes.tags[input] == AST_NODE_FOR_RANGE) { - // Range input (AstGen.zig:6892-6916). + // Diagnostic: cannot capture reference to range + // (AstGen.zig:6893-6895). + if (capture_is_ref) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + + // Range input (AstGen.zig:6896-6916). AstData range_nd = tree->nodes.datas[input]; uint32_t start_node = range_nd.lhs; uint32_t end_node = range_nd.rhs; // AstGen.zig:6897-6902: expr with .rl = .{ .ty = .usize_type } - ResultLoc usize_rl - = { .tag = RL_TY, .data = ZIR_REF_USIZE_TYPE, .src_node = 0 }; + ResultLoc usize_rl = { + .tag = RL_TY, + .data = ZIR_REF_USIZE_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE, + }; uint32_t start_val = exprRl(gz, scope, usize_rl, start_node); uint32_t end_val = ZIR_REF_NONE; @@ -5882,10 +5922,17 @@ static uint32_t forExpr( end_val = exprRl(gz, scope, usize_rl, end_node); } + // Diagnostic: discard of unbounded counter + // (AstGen.zig:6904-6906). + if (end_val == ZIR_REF_NONE && is_discard) { + SET_ERROR(ag); + } + if (end_val == ZIR_REF_NONE) { lens[i][0] = ZIR_REF_NONE; lens[i][1] = ZIR_REF_NONE; } else { + any_len_checks = true; lens[i][0] = start_val; lens[i][1] = end_val; } @@ -5905,12 +5952,19 @@ static uint32_t forExpr( } else { // Regular indexable (AstGen.zig:6918-6923). uint32_t indexable = expr(gz, scope, input); + any_len_checks = true; indexables[i] = indexable; lens[i][0] = indexable; lens[i][1] = ZIR_REF_NONE; } } + // Error if no length checks exist (AstGen.zig:6927-6929). + if (!any_len_checks) { + SET_ERROR(ag); + return ZIR_REF_VOID_VALUE; + } + // Emit for_len as MultiOp (AstGen.zig:6933-6942). uint32_t len; { @@ -5928,12 +5982,16 @@ static uint32_t forExpr( len = addInstruction(gz, ZIR_INST_FOR_LEN, data); } - // Create loop (AstGen.zig:6944-6956). + // Create loop (AstGen.zig:6944-6946). ZirInstTag loop_tag = is_inline ? ZIR_INST_BLOCK_INLINE : ZIR_INST_LOOP; uint32_t loop_inst = makeBlockInst(ag, loop_tag, gz, node); + // Issue 3: append loop_inst to parent_gz immediately (AstGen.zig:6946). + gzAppendInstruction(gz, loop_inst); GenZir loop_scope = makeSubBlock(gz, scope); loop_scope.is_inline = is_inline; + // Issue 1: set break_result_info (AstGen.zig:6950). + loop_scope.break_result_info = break_rl; // Load index (AstGen.zig:6955-6956). // We need to finish loop_scope later once we have the deferred refs from @@ -5957,6 +6015,11 @@ static uint32_t forExpr( loop_scope.break_block = loop_inst; loop_scope.continue_block = cond_block; // AstGen.zig:6974 + // Issue 4: set label on loop_scope (AstGen.zig:6975-6980). + if (label_token != UINT32_MAX) { + loop_scope.label_token = label_token; + loop_scope.label_block_inst = loop_inst; + } // Then branch: loop body (AstGen.zig:6982-7065). GenZir then_scope = makeSubBlock(gz, &cond_scope.base); @@ -5974,16 +6037,7 @@ static uint32_t forExpr( capture_token = ident_tok + 2; // Check if discard (AstGen.zig:6999). - uint32_t ts = tree->tokens.starts[ident_tok]; - bool is_discard = (tree->source[ts] == '_' - && (ts + 1 >= tree->source_len - || !((tree->source[ts + 1] >= 'a' - && tree->source[ts + 1] <= 'z') - || (tree->source[ts + 1] >= 'A' - && tree->source[ts + 1] <= 'Z') - || tree->source[ts + 1] == '_' - || (tree->source[ts + 1] >= '0' - && tree->source[ts + 1] <= '9')))); + bool is_discard = tokenIsUnderscore(tree, ident_tok); if (is_discard) continue; @@ -6044,10 +6098,37 @@ static uint32_t forExpr( addBreak(&then_scope, break_tag, cond_block, ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE); - // Else branch: break out of loop (AstGen.zig:7066-7091). + // Else branch (AstGen.zig:7066-7091). GenZir else_scope = makeSubBlock(gz, &cond_scope.base); - addBreak(&else_scope, break_tag, loop_inst, ZIR_REF_VOID_VALUE, - AST_NODE_OFFSET_NONE); + + if (else_node != 0) { + // Issue 2: evaluate else expression (AstGen.zig:7069-7081). + // Remove continue/break blocks so that control flow applies + // to outer loops (AstGen.zig:7073-7074). + loop_scope.continue_block = UINT32_MAX; + loop_scope.break_block = UINT32_MAX; + uint32_t else_result = fullBodyExpr(&else_scope, &else_scope.base, + loop_scope.break_result_info, else_node); + if (is_statement) { + addEnsureResult(&else_scope, else_result, else_node); + } + if (!endsWithNoReturn(&else_scope)) { + addBreak(&else_scope, break_tag, loop_inst, else_result, + (int32_t)else_node - (int32_t)gz->decl_node_index); + } + } else { + // No else: break with void (AstGen.zig:7082-7085). + uint32_t void_result + = rvalue(&else_scope, rl, ZIR_REF_VOID_VALUE, node); + addBreak(&else_scope, break_tag, loop_inst, void_result, + AST_NODE_OFFSET_NONE); + } + + // Issue 4: check unused label (AstGen.zig:7087-7091). + if (label_token != UINT32_MAX) { + // Note: upstream checks loop_scope.label.used; we don't track usage + // yet, so skip the "unused for loop label" error for now. + } setCondBrPayload(ag, condbr, cond, &then_scope, &else_scope); @@ -6075,9 +6156,13 @@ static uint32_t forExpr( setBlockBody(ag, &loop_scope, loop_inst); } - gzAppendInstruction(gz, loop_inst); - uint32_t result = loop_inst + ZIR_REF_START_INDEX; + // Issue 1: apply rvalue if needed (AstGen.zig:7116-7119). + uint32_t result; + if (need_result_rvalue) + result = rvalue(gz, rl, loop_inst + ZIR_REF_START_INDEX, node); + else + result = loop_inst + ZIR_REF_START_INDEX; // Emit ensure_result_used when used as statement (AstGen.zig:7121-7123). if (is_statement) { @@ -7617,7 +7702,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope, break; case AST_NODE_FOR_SIMPLE: case AST_NODE_FOR: - (void)forExpr(gz, cur_scope, inner_node, true); + (void)forExpr(gz, cur_scope, RL_NONE_VAL, inner_node, true); break; default: { // Expression statement (AstGen.zig:2627 unusedResultExpr).