Report an uniterable for range once (#7661)

Building a `for` loop looks `Core.Iterate` up twice: once for
`NewCursor` to make the cursor, and again for `Next` to advance it. A
range that implements neither failed both lookups and reported both, so
a loop over something that isn't iterable produced two errors saying the
same thing about the same expression.

The second lookup is skipped when the first already failed, which is
what `BuildBinaryOperator`'s `diagnose` parameter is for. The
`ErrorInst` it returns instead does not reach the produced SemIR: the
loop is abandoned on the error either way.

Assisted-by: Claude Code

---------

Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Chandler Carruth
2026-08-21 04:09:16 +00:00
committed by GitHub
co-authored by josh11b Richard Smith
parent c588eadb57
commit 4172f4d3f2
3 changed files with 11 additions and 15 deletions
+5 -1
View File
@@ -224,11 +224,15 @@ auto HandleParseNode(Context& context, Parse::ForHeaderId node_id) -> bool {
context, node_id,
{.type_id = GetPointerType(context, cursor_type_inst_id),
.lvalue_id = cursor_var_id});
// A range that implements neither fails both lookups; reporting the second
// would say the same thing about the same expression a second time.
// TODO: We should only perform the impl lookup once.
auto element_id =
BuildBinaryOperator(context, node_id,
{.interface_name = CoreIdentifier::Iterate,
.op_name = CoreIdentifier::Next},
range_id, cursor_addr_id);
range_id, cursor_addr_id,
/*diagnose=*/cursor_id != SemIR::ErrorInst::InstId);
// We need to convert away from an initializing expression in order to call
// `HasValue` and then separately pattern-match against the element.
// TODO: Instead, form a `.Some(pattern_id)` pattern and pattern-match against