about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-09-29 11:04:47 +0000
committerbors <bors@rust-lang.org>2022-09-29 11:04:47 +0000
commit97f8f4a3da6cfbac4d914eca7d6a87da8f27901c (patch)
treedbdf9078b8d8d2df55ab7a35e0e913bd9e9790dd
parentad752bd52115186ab5ebe2fd27f09867112747c1 (diff)
parent6d8903ae5f3d3a16a96e0a3e3f32ce88d7998392 (diff)
downloadrust-97f8f4a3da6cfbac4d914eca7d6a87da8f27901c.tar.gz
rust-97f8f4a3da6cfbac4d914eca7d6a87da8f27901c.zip
Auto merge of #13311 - lowr:fix/for-loop-item-resolution, r=Veykril
fix: infer for-loop item type with `IntoIterator` and `Iterator`

Part of #13299

We've been inferring the type of the yielded values in for-loop as `<T as IntoIterator>::Item`. We infer the correct type most of the time when we normalize the projection type, but it turns out not always. We should infer the type as `<<T as IntoIterator>::IntoIter as Iterator>::Item`.

When one specifies `IntoIter` assoc type of `IntoIterator` but not `Item` in generic bounds, we fail to normalize `<T as IntoIterator>::Item` (even though `IntoIter` is defined like so: `type IntoIter: Iterator<Item = Self::Item>` - rustc does *not* normalize projections based on other projection's bound I believe; see [this playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e88e19385094cb98fadbf647b4c2082e)).

Note that this doesn't fully fix # 13299 - given the following code, chalk can normalize `<I as IntoIterator>::IntoIter` to `S`, but cannot normalize `<S as Iterator>::Item` to `i32`.

```rust
struct S;
impl Iterator for S { type Item = i32; /* ... */ }
fn f<I: IntoIterator<IntoIter = S>>(it: I) {
    for elem in it {}
      //^^^^{unknown}
}
```

This is because chalk finds multiple answers that satisfy the query `AliasEq(<S as Iterator>::Item = ?X`: `?X = i32` and `?X = <I as IntoIterator>::Item` - which are supposed to be the same type due to the aforementioned bound on `IntoIter` but chalk is unable to figure it out.
-rw-r--r--crates/hir-expand/src/name.rs1
-rw-r--r--crates/hir-ty/src/infer.rs6
-rw-r--r--crates/hir-ty/src/infer/expr.rs4
-rw-r--r--crates/hir-ty/src/tests/traits.rs12
-rw-r--r--crates/ide/src/inlay_hints.rs9
5 files changed, 29 insertions, 3 deletions
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 4ce21a57967..2679a1c3602 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -263,6 +263,7 @@ pub mod known {
         Iterator,
         IntoIterator,
         Item,
+        IntoIter,
         Try,
         Ok,
         Future,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 039824694ac..25179afaca7 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -883,6 +883,12 @@ impl<'a> InferenceContext<'a> {
     fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
         let path = path![core::iter::IntoIterator];
         let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
+        self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
+    }
+
+    fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
+        let path = path![core::iter::Iterator];
+        let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
         self.db.trait_data(trait_).associated_type_by_name(&name![Item])
     }
 
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index e3d6be23e65..2643baf8a32 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -207,8 +207,10 @@ impl<'a> InferenceContext<'a> {
             }
             &Expr::For { iterable, body, pat, label } => {
                 let iterable_ty = self.infer_expr(iterable, &Expectation::none());
-                let pat_ty =
+                let into_iter_ty =
                     self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
+                let pat_ty =
+                    self.resolve_associated_type(into_iter_ty, self.resolve_iterator_item());
 
                 self.infer_pat(pat, &pat_ty, BindingMode::default());
                 self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 21a86319763..555b6972fb7 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -279,6 +279,10 @@ fn test() {
 pub mod iter {
     pub trait IntoIterator {
         type Item;
+        type IntoIter: Iterator<Item = Self::Item>;
+    }
+    pub trait Iterator {
+        type Item;
     }
 }
 pub mod prelude {
@@ -297,7 +301,13 @@ pub mod collections {
     }
 
     impl<T> IntoIterator for Vec<T> {
-        type Item=T;
+        type Item = T;
+        type IntoIter = IntoIter<T>;
+    }
+
+    struct IntoIter<T> {}
+    impl<T> Iterator for IntoIter<T> {
+        type Item = T;
     }
 }
 "#,
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 08363d21e89..34d8bf67a30 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -2024,7 +2024,14 @@ impl<T> Vec<T> {
 }
 
 impl<T> IntoIterator for Vec<T> {
-    type Item=T;
+    type Item = T;
+    type IntoIter = IntoIter<T>;
+}
+
+struct IntoIter<T> {}
+
+impl<T> Iterator for IntoIter<T> {
+    type Item = T;
 }
 
 fn main() {