Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1771,8 +1771,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
let pat = self.lower_pat(pat);
let for_span =
self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None);
let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None);
let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None);
let for_ctxt = for_span.ctxt();

// Try to point both the head and pat spans to their position in the for loop
// rather than inside a macro.
let head_span =
head.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(head.span).with_ctxt(for_ctxt);
Comment on lines +1778 to +1779
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The currently failing tests/ui/suggest-dereferences/invalid-suggest-deref-issue127590 can be fixed by making a second mark rather than reusing for_ctxt. I'm not exactly clear why this matters since the only change this makes is using two identical, but separate SyntaxContexts.

Ideally only a single SyntaxContext would be created since there's only one for loop.

Copy link
Contributor Author

@Jarcho Jarcho Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Narrowed this down to being different expansion call sites. If we replace the for loop with a call to into_iter then it always produces three diagnostics.

Errors
error[E0277]: `&std::slice::Iter<'_, i32>` is not an iterator
  --> src/main.rs:2:59
   |
 2 |     IntoIterator::into_iter(std::iter::zip([1i32].iter(), &[1i32].iter()));
   |                             --------------                ^^^^^^^^^^^^^^ `&std::slice::Iter<'_, i32>` is not an iterator
   |                             |
   |                             required by a bound introduced by this call
   |
   = help: the trait `Iterator` is not implemented for `&std::slice::Iter<'_, i32>`
   = note: required for `&std::slice::Iter<'_, i32>` to implement `IntoIterator`
note: required by a bound in `std::iter::zip`
  --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/adapters/zip.rs:70:8
   |
67 | pub fn zip<A, B>(a: A, b: B) -> Zip<A::IntoIter, B::IntoIter>
   |        --- required by a bound in this function
...
70 |     B: IntoIterator,
   |        ^^^^^^^^^^^^ required by this bound in `zip`
help: consider removing the leading `&`-reference
   |
 2 -     IntoIterator::into_iter(std::iter::zip([1i32].iter(), &[1i32].iter()));
 2 +     IntoIterator::into_iter(std::iter::zip([1i32].iter(), [1i32].iter()));
   |
help: consider changing this borrow's mutability
   |
 2 |     IntoIterator::into_iter(std::iter::zip([1i32].iter(), &mut [1i32].iter()));
   |                                                            +++

error[E0277]: `&std::slice::Iter<'_, i32>` is not an iterator
 --> src/main.rs:2:29
  |
2 |     IntoIterator::into_iter(std::iter::zip([1i32].iter(), &[1i32].iter()));
  |     ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&std::slice::Iter<'_, i32>` is not an iterator
  |     |
  |     required by a bound introduced by this call
  |
  = help: the trait `Iterator` is not implemented for `&std::slice::Iter<'_, i32>`
  = help: the trait `Iterator` is implemented for `std::slice::Iter<'_, T>`
  = note: `Iterator` is implemented for `&mut std::slice::Iter<'_, i32>`, but not for `&std::slice::Iter<'_, i32>`
  = note: required for `Zip<std::slice::Iter<'_, i32>, &std::slice::Iter<'_, i32>>` to implement `Iterator`
  = note: required for `Zip<std::slice::Iter<'_, i32>, &std::slice::Iter<'_, i32>>` to implement `IntoIterator`

error[E0277]: `&std::slice::Iter<'_, i32>` is not an iterator
 --> src/main.rs:2:5
  |
2 |     IntoIterator::into_iter(std::iter::zip([1i32].iter(), &[1i32].iter()));
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&std::slice::Iter<'_, i32>` is not an iterator
  |
  = help: the trait `Iterator` is not implemented for `&std::slice::Iter<'_, i32>`
  = help: the trait `Iterator` is implemented for `std::slice::Iter<'_, T>`
  = note: `Iterator` is implemented for `&mut std::slice::Iter<'_, i32>`, but not for `&std::slice::Iter<'_, i32>`
  = note: required for `Zip<std::slice::Iter<'_, i32>, &std::slice::Iter<'_, i32>>` to implement `Iterator`
  = note: required for `Zip<std::slice::Iter<'_, i32>, &std::slice::Iter<'_, i32>>` to implement `IntoIterator`

The third error is filtered on for loops since when deduplicating obligation failures if the cause span is in a desugaring then the call site span is used instead. Originally the call site span for the into_iter call is the head expression which causes it to be suppressed since this matches the second error. With this PR the call site span is the for loop itself which doesn't match the previous two errors.

If this is actually a problem I can mark the head expression separately. I don't see this as too big of a deal since the second error will be present in either case and it's just as useless as the third one.

let pat_span =
pat.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(pat.span).with_ctxt(for_ctxt);

let loop_hir_id = self.lower_node_id(e.id);
let label = self.lower_label(opt_label, e.id, loop_hir_id);
Expand Down
26 changes: 26 additions & 0 deletions tests/ui/for/iter_from_mac_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
macro_rules! deref {
($e:expr) => { *$e };
}

fn f1<'a>(mut iter: Box<dyn Iterator<Item=&'a mut u8>>) {
for item in deref!(iter) { *item = 0 }
//~^ ERROR `dyn Iterator<Item = &'a mut u8>` is not an iterator
}

fn f2(x: &mut i32) {
for _item in deref!(x) {}
//~^ ERROR `i32` is not an iterator
}

struct Wrapped(i32);

macro_rules! borrow_deref {
($e:expr) => { &mut *$e };
}

fn f3<'a>(mut iter: Box<dyn Iterator<Item=&'a mut i32>>) {
for Wrapped(item) in borrow_deref!(iter) { *item = 0 }
//~^ ERROR mismatched types
}

fn main() {}
35 changes: 35 additions & 0 deletions tests/ui/for/iter_from_mac_call.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error[E0277]: `dyn Iterator<Item = &'a mut u8>` is not an iterator
--> $DIR/iter_from_mac_call.rs:6:17
|
LL | for item in deref!(iter) { *item = 0 }
| ^^^^^^^^^^^^ the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
|
= note: the trait bound `dyn Iterator<Item = &'a mut u8>: IntoIterator` is not satisfied
= note: required for `dyn Iterator<Item = &'a mut u8>` to implement `IntoIterator`
help: consider mutably borrowing here
|
LL | for item in &mut deref!(iter) { *item = 0 }
| ++++

error[E0277]: `i32` is not an iterator
--> $DIR/iter_from_mac_call.rs:11:18
|
LL | for _item in deref!(x) {}
| ^^^^^^^^^ `i32` is not an iterator
|
= help: the trait `Iterator` is not implemented for `i32`
= note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end`
= note: required for `i32` to implement `IntoIterator`

error[E0308]: mismatched types
--> $DIR/iter_from_mac_call.rs:22:9
|
LL | for Wrapped(item) in borrow_deref!(iter) { *item = 0 }
| ^^^^^^^^^^^^^ ------------------- this is an iterator with items of type `&mut i32`
| |
| expected `i32`, found `Wrapped`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,30 @@ help: the trait `Iterator` is implemented for `std::slice::Iter<'_, T>`
= note: required for `Zip<std::slice::Iter<'_, {integer}>, &std::slice::Iter<'_, {integer}>>` to implement `IntoIterator`
= note: this error originates in the macro `iterator` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 4 previous errors
error[E0277]: `&std::slice::Iter<'_, {integer}>` is not an iterator
--> $DIR/invalid-suggest-deref-issue-127590.rs:6:24
|
LL | for (src, dest) in std::iter::zip(fields.iter(), &variant.iter()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&std::slice::Iter<'_, {integer}>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&std::slice::Iter<'_, {integer}>`
help: the trait `Iterator` is implemented for `std::slice::Iter<'_, T>`
--> $SRC_DIR/core/src/slice/iter.rs:LL:COL
= note: required for `&std::slice::Iter<'_, {integer}>` to implement `IntoIterator`
= note: this error originates in the macro `iterator` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: `&std::slice::Iter<'_, {integer}>` is not an iterator
--> $DIR/invalid-suggest-deref-issue-127590.rs:13:24
|
LL | for (src, dest) in std::iter::zip(fields.iter(), &variant.iter().clone()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&std::slice::Iter<'_, {integer}>` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&std::slice::Iter<'_, {integer}>`
help: the trait `Iterator` is implemented for `std::slice::Iter<'_, T>`
--> $SRC_DIR/core/src/slice/iter.rs:LL:COL
= note: required for `&std::slice::Iter<'_, {integer}>` to implement `IntoIterator`
= note: this error originates in the macro `iterator` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0277`.
Loading