about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2023-03-29 14:31:32 +0200
committerLukas Wirth <lukastw97@gmail.com>2023-03-29 14:49:06 +0200
commitbea1c71f833ad9b8dacd98b1f950000ff9ab2f74 (patch)
tree19d22f00822f116eac78ea7ed1ffe07e8d6536ff
parent7a98e24777d127c8d3cdb322aa7e9ea4f4dba5e3 (diff)
downloadrust-bea1c71f833ad9b8dacd98b1f950000ff9ab2f74.tar.gz
rust-bea1c71f833ad9b8dacd98b1f950000ff9ab2f74.zip
Use struct_tail_without_normalization in Expectation::rvalue_hint
-rw-r--r--crates/hir-ty/src/infer.rs66
-rw-r--r--crates/hir-ty/src/infer/expr.rs6
-rw-r--r--crates/hir-ty/src/tests/coercion.rs5
-rw-r--r--lib/la-arena/src/map.rs8
4 files changed, 71 insertions, 14 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 493f45d40ce..ab08593bcf6 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -13,8 +13,8 @@
 //! to certain types. To record this, we use the union-find implementation from
 //! the `ena` crate, which is extracted from rustc.
 
-use std::ops::Index;
 use std::sync::Arc;
+use std::{convert::identity, ops::Index};
 
 use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
 use either::Either;
@@ -791,6 +791,65 @@ impl<'a> InferenceContext<'a> {
         self.table.unify(ty1, ty2)
     }
 
+    /// Attempts to returns the deeply last field of nested structures, but
+    /// does not apply any normalization in its search. Returns the same type
+    /// if input `ty` is not a structure at all.
+    fn struct_tail_without_normalization(&mut self, ty: Ty) -> Ty {
+        self.struct_tail_with_normalize(ty, identity)
+    }
+
+    /// Returns the deeply last field of nested structures, or the same type if
+    /// not a structure at all. Corresponds to the only possible unsized field,
+    /// and its type can be used to determine unsizing strategy.
+    ///
+    /// This is parameterized over the normalization strategy (i.e. how to
+    /// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity
+    /// function to indicate no normalization should take place.
+    fn struct_tail_with_normalize(
+        &mut self,
+        mut ty: Ty,
+        mut normalize: impl FnMut(Ty) -> Ty,
+    ) -> Ty {
+        // FIXME: fetch the limit properly
+        let recursion_limit = 10;
+        for iteration in 0.. {
+            if iteration > recursion_limit {
+                return self.err_ty();
+            }
+            match ty.kind(Interner) {
+                TyKind::Adt(chalk_ir::AdtId(hir_def::AdtId::StructId(struct_id)), substs) => {
+                    match self.db.field_types((*struct_id).into()).values().next_back().cloned() {
+                        Some(field) => {
+                            ty = field.substitute(Interner, substs);
+                        }
+                        None => break,
+                    }
+                }
+                TyKind::Adt(..) => break,
+                TyKind::Tuple(_, substs) => {
+                    match substs
+                        .as_slice(Interner)
+                        .split_last()
+                        .and_then(|(last_ty, _)| last_ty.ty(Interner))
+                    {
+                        Some(last_ty) => ty = last_ty.clone(),
+                        None => break,
+                    }
+                }
+                TyKind::Alias(..) => {
+                    let normalized = normalize(ty.clone());
+                    if ty == normalized {
+                        return ty;
+                    } else {
+                        ty = normalized;
+                    }
+                }
+                _ => break,
+            }
+        }
+        ty
+    }
+
     /// Recurses through the given type, normalizing associated types mentioned
     /// in it by replacing them by type variables and registering obligations to
     /// resolve later. This should be done once for every type we get from some
@@ -1138,9 +1197,8 @@ impl Expectation {
     /// which still is useful, because it informs integer literals and the like.
     /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
     /// for examples of where this comes up,.
-    fn rvalue_hint(table: &mut unify::InferenceTable<'_>, ty: Ty) -> Self {
-        // FIXME: do struct_tail_without_normalization
-        match table.resolve_ty_shallow(&ty).kind(Interner) {
+    fn rvalue_hint(ctx: &mut InferenceContext<'_>, ty: Ty) -> Self {
+        match ctx.struct_tail_without_normalization(ty.clone()).kind(Interner) {
             TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
             _ => Expectation::has_type(ty),
         }
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index be4af98de50..35372ee9a90 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -643,7 +643,7 @@ impl<'a> InferenceContext<'a> {
                         // FIXME: record type error - expected reference but found ptr,
                         // which cannot be coerced
                     }
-                    Expectation::rvalue_hint(&mut self.table, Ty::clone(exp_inner))
+                    Expectation::rvalue_hint(self, Ty::clone(exp_inner))
                 } else {
                     Expectation::none()
                 };
@@ -998,7 +998,7 @@ impl<'a> InferenceContext<'a> {
                 .filter(|(e_adt, _)| e_adt == &box_id)
                 .map(|(_, subts)| {
                     let g = subts.at(Interner, 0);
-                    Expectation::rvalue_hint(table, Ty::clone(g.assert_ty_ref(Interner)))
+                    Expectation::rvalue_hint(self, Ty::clone(g.assert_ty_ref(Interner)))
                 })
                 .unwrap_or_else(Expectation::none);
 
@@ -1593,7 +1593,7 @@ impl<'a> InferenceContext<'a> {
                 // the parameter to coerce to the expected type (for example in
                 // `coerce_unsize_expected_type_4`).
                 let param_ty = self.normalize_associated_types_in(param_ty);
-                let expected = Expectation::rvalue_hint(&mut self.table, expected_ty);
+                let expected = Expectation::rvalue_hint(self, expected_ty);
                 // infer with the expected type we have...
                 let ty = self.infer_expr_inner(arg, &expected);
 
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 9f624cc32c0..ce1a22cc51b 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -536,7 +536,6 @@ fn test() {
 
 #[test]
 fn coerce_unsize_generic() {
-    // FIXME: fix the type mismatches here
     check(
         r#"
 //- minicore: coerce_unsized
@@ -545,9 +544,9 @@ struct Bar<T>(Foo<T>);
 
 fn test() {
     let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
-                                   //^^^^^^^^^ expected [usize], got [usize; 3]
+                         //^^^^^^^^^^^^^^^^^^^^^ expected &Foo<[usize]>, got &Foo<[i32; 3]>
     let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
-                                       //^^^^^^^^^ expected [usize], got [usize; 3]
+                         //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]>
 }
 "#,
     );
diff --git a/lib/la-arena/src/map.rs b/lib/la-arena/src/map.rs
index 7fff2b09c97..610f7d92d65 100644
--- a/lib/la-arena/src/map.rs
+++ b/lib/la-arena/src/map.rs
@@ -72,17 +72,17 @@ impl<T, V> ArenaMap<Idx<T>, V> {
     }
 
     /// Returns an iterator over the values in the map.
-    pub fn values(&self) -> impl Iterator<Item = &V> {
+    pub fn values(&self) -> impl Iterator<Item = &V> + DoubleEndedIterator {
         self.v.iter().filter_map(|o| o.as_ref())
     }
 
     /// Returns an iterator over mutable references to the values in the map.
-    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
+    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> + DoubleEndedIterator {
         self.v.iter_mut().filter_map(|o| o.as_mut())
     }
 
     /// Returns an iterator over the arena indexes and values in the map.
-    pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> {
+    pub fn iter(&self) -> impl Iterator<Item = (Idx<T>, &V)> + DoubleEndedIterator {
         self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
     }
 
@@ -96,7 +96,7 @@ impl<T, V> ArenaMap<Idx<T>, V> {
 
     /// Returns an iterator over the arena indexes and values in the map.
     // FIXME: Implement `IntoIterator` trait.
-    pub fn into_iter(self) -> impl Iterator<Item = (Idx<T>, V)> {
+    pub fn into_iter(self) -> impl Iterator<Item = (Idx<T>, V)> + DoubleEndedIterator {
         self.v.into_iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o?)))
     }