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 -9
View File
@@ -442,10 +442,6 @@ fn TestRangeFor(var m: Cpp.DiffMutableADL.MutableRange) {
var sum: Cpp.DiffMutableADL.ValueType = Cpp.DiffMutableADL.ValueType.ValueType();
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_adl_returns_different_types_mutable.carbon:[[@LINE+8]]:7: error: cannot access member of interface `Core.Iterate` in type `Cpp.DiffMutableADL.MutableRange` that does not implement that interface [MissingImplInMemberAccess]
// CHECK:STDERR: for (i: Cpp.DiffMutableADL.ValueType in m) {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_adl_returns_different_types_mutable.carbon:[[@LINE+4]]:7: error: cannot access member of interface `Core.Iterate` in type `Cpp.DiffMutableADL.MutableRange` that does not implement that interface [MissingImplInMemberAccess]
// CHECK:STDERR: for (i: Cpp.DiffMutableADL.ValueType in m) {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -4085,16 +4081,16 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd,
// CHECK:STDOUT: br !for.next
// CHECK:STDOUT:
// CHECK:STDOUT: !for.next:
// CHECK:STDOUT: %addr.loc98: <error> = addr_of <error> [concrete = <error>]
// CHECK:STDOUT: %addr.loc94: <error> = addr_of <error> [concrete = <error>]
// CHECK:STDOUT: %HasValue.ref: <error> = name_ref HasValue, <error> [concrete = <error>]
// CHECK:STDOUT: if <error> br !for.body else br !for.done
// CHECK:STDOUT:
// CHECK:STDOUT: !for.body:
// CHECK:STDOUT: %Get.ref: <error> = name_ref Get, <error> [concrete = <error>]
// CHECK:STDOUT: %.loc98: type = splice_block %ValueType.ref.loc98 [concrete = constants.%ValueType] {
// CHECK:STDOUT: %Cpp.ref.loc98: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %DiffMutableADL.ref.loc98: <namespace> = name_ref DiffMutableADL, imports.%DiffMutableADL [concrete = imports.%DiffMutableADL]
// CHECK:STDOUT: %ValueType.ref.loc98: type = name_ref ValueType, imports.%ValueType.decl [concrete = constants.%ValueType]
// CHECK:STDOUT: %.loc94: type = splice_block %ValueType.ref.loc94 [concrete = constants.%ValueType] {
// CHECK:STDOUT: %Cpp.ref.loc94: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %DiffMutableADL.ref.loc94: <namespace> = name_ref DiffMutableADL, imports.%DiffMutableADL [concrete = imports.%DiffMutableADL]
// CHECK:STDOUT: %ValueType.ref.loc94: type = name_ref ValueType, imports.%ValueType.decl [concrete = constants.%ValueType]
// CHECK:STDOUT: }
// CHECK:STDOUT: %i: %ValueType = wrapper_binding i, <error> [concrete = <error>]
// CHECK:STDOUT: %sum.ref: ref %ValueType = name_ref sum, %sum