Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer.rs')
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 66 |
1 files changed, 62 insertions, 4 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 493f45d40c..ab08593bcf 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), } |