From de39d221a15c0a146ed8adbdb1616692180948bb Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 Feb 2020 18:24:18 +0100 Subject: [PATCH 1/8] Implement unsize coercion using proper trait solving --- crates/ra_hir_ty/src/infer.rs | 7 - crates/ra_hir_ty/src/infer/coerce.rs | 205 ++++--------------------- crates/ra_hir_ty/src/tests/coercion.rs | 82 ++++++++++ crates/ra_hir_ty/src/traits.rs | 2 + crates/ra_hir_ty/src/traits/builtin.rs | 61 +++++++- 5 files changed, 170 insertions(+), 187 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 76069eb9c2..6e1d268dea 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -206,12 +206,6 @@ struct InferenceContext<'a, D: HirDatabase> { /// closures, but currently this is the only field that will change there, /// so it doesn't make sense. return_ty: Ty, - - /// Impls of `CoerceUnsized` used in coercion. - /// (from_ty_ctor, to_ty_ctor) => coerce_generic_index - // FIXME: Use trait solver for this. - // Chalk seems unable to work well with builtin impl of `Unsize` now. - coerce_unsized_map: FxHashMap<(TypeCtor, TypeCtor), usize>, } impl<'a, D: HirDatabase> InferenceContext<'a, D> { @@ -222,7 +216,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { obligations: Vec::default(), return_ty: Ty::Unknown, // set in collect_fn_signature trait_env: TraitEnvironment::lower(db, &resolver), - coerce_unsized_map: Self::init_coerce_unsized_map(db, &resolver), db, owner, body: db.body(owner), diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index fb6a51b127..95ac3c7132 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -4,11 +4,12 @@ //! //! See: https://doc.rust-lang.org/nomicon/coercions.html -use hir_def::{lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutability, AdtId}; -use rustc_hash::FxHashMap; +use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; use test_utils::tested_by; -use crate::{autoderef, db::HirDatabase, Substs, Ty, TypeCtor, TypeWalk}; +use crate::{ + autoderef, db::HirDatabase, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor, +}; use super::{unify::TypeVarValue, InEnvironment, InferTy, InferenceContext}; @@ -39,44 +40,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } - pub(super) fn init_coerce_unsized_map( - db: &'a D, - resolver: &Resolver, - ) -> FxHashMap<(TypeCtor, TypeCtor), usize> { - let krate = resolver.krate().unwrap(); - let impls = match db.lang_item(krate, "coerce_unsized".into()) { - Some(LangItemTarget::TraitId(trait_)) => db.impls_for_trait(krate, trait_), - _ => return FxHashMap::default(), - }; - - impls - .iter() - .filter_map(|&impl_id| { - let trait_ref = db.impl_trait(impl_id)?; - - // `CoerseUnsized` has one generic parameter for the target type. - let cur_from_ty = trait_ref.value.substs.0.get(0)?; - let cur_to_ty = trait_ref.value.substs.0.get(1)?; - - match (&cur_from_ty, cur_to_ty) { - (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => { - // FIXME: We return the first non-equal bound as the type parameter to coerce to unsized type. - // This works for smart-pointer-like coercion, which covers all impls from std. - st1.iter().zip(st2.iter()).enumerate().find_map(|(i, (ty1, ty2))| { - match (ty1, ty2) { - (Ty::Bound(idx1), Ty::Bound(idx2)) if idx1 != idx2 => { - Some(((*ctor1, *ctor2), i)) - } - _ => None, - } - }) - } - _ => None, - } - }) - .collect() - } - fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { match (&from_ty, to_ty) { // Never type will make type variable to fallback to Never Type instead of Unknown. @@ -157,154 +120,38 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option { - let (ctor1, st1, ctor2, st2) = match (from_ty, to_ty) { - (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => (ctor1, st1, ctor2, st2), + let krate = self.resolver.krate().unwrap(); + let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { + Some(LangItemTarget::TraitId(trait_)) => trait_, _ => return None, }; - let coerce_generic_index = *self.coerce_unsized_map.get(&(*ctor1, *ctor2))?; - - // Check `Unsize` first - match self.check_unsize_and_coerce( - st1.0.get(coerce_generic_index)?, - st2.0.get(coerce_generic_index)?, - 0, - ) { - Some(true) => {} - ret => return ret, + let generic_params = crate::utils::generics(self.db, coerce_unsized_trait.into()); + if generic_params.len() != 2 { + // The CoerceUnsized trait should have two generic params: Self and T. + return None; } - let ret = st1 - .iter() - .zip(st2.iter()) - .enumerate() - .filter(|&(idx, _)| idx != coerce_generic_index) - .all(|(_, (ty1, ty2))| self.unify(ty1, ty2)); + let substs = Substs::build_for_generics(&generic_params) + .push(from_ty.clone()) + .push(to_ty.clone()) + .build(); + let trait_ref = TraitRef { trait_: coerce_unsized_trait, substs }; + let goal = InEnvironment::new(self.trait_env.clone(), Obligation::Trait(trait_ref)); - Some(ret) - } + let canonicalizer = self.canonicalizer(); + let canonicalized = canonicalizer.canonicalize_obligation(goal); - /// Check if `from_ty: Unsize`, and coerce to `to_ty` if it holds. - /// - /// It should not be directly called. It is only used by `try_coerce_unsized`. - /// - /// See: https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html - fn check_unsize_and_coerce(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> Option { - if depth > 1000 { - panic!("Infinite recursion in coercion"); - } + let solution = self.db.trait_solve(krate, canonicalized.value.clone())?; - match (&from_ty, &to_ty) { - // `[T; N]` -> `[T]` - (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => { - Some(self.unify(&st1[0], &st2[0])) + match solution { + Solution::Unique(v) => { + canonicalized.apply_solution(self, v.0); } + _ => return None, + }; - // `T` -> `dyn Trait` when `T: Trait` - (_, Ty::Dyn(_)) => { - // FIXME: Check predicates - Some(true) - } - - // `(..., T)` -> `(..., U)` when `T: Unsize` - ( - ty_app!(TypeCtor::Tuple { cardinality: len1 }, st1), - ty_app!(TypeCtor::Tuple { cardinality: len2 }, st2), - ) => { - if len1 != len2 || *len1 == 0 { - return None; - } - - match self.check_unsize_and_coerce( - st1.last().unwrap(), - st2.last().unwrap(), - depth + 1, - ) { - Some(true) => {} - ret => return ret, - } - - let ret = st1[..st1.len() - 1] - .iter() - .zip(&st2[..st2.len() - 1]) - .all(|(ty1, ty2)| self.unify(ty1, ty2)); - - Some(ret) - } - - // Foo<..., T, ...> is Unsize> if: - // - T: Unsize - // - Foo is a struct - // - Only the last field of Foo has a type involving T - // - T is not part of the type of any other fields - // - Bar: Unsize>, if the last field of Foo has type Bar - ( - ty_app!(TypeCtor::Adt(AdtId::StructId(struct1)), st1), - ty_app!(TypeCtor::Adt(AdtId::StructId(struct2)), st2), - ) if struct1 == struct2 => { - let field_tys = self.db.field_types((*struct1).into()); - let struct_data = self.db.struct_data(*struct1); - - let mut fields = struct_data.variant_data.fields().iter(); - let (last_field_id, _data) = fields.next_back()?; - - // Get the generic parameter involved in the last field. - let unsize_generic_index = { - let mut index = None; - let mut multiple_param = false; - field_tys[last_field_id].value.walk(&mut |ty| { - if let &Ty::Bound(idx) = ty { - if index.is_none() { - index = Some(idx); - } else if Some(idx) != index { - multiple_param = true; - } - } - }); - - if multiple_param { - return None; - } - index? - }; - - // Check other fields do not involve it. - let mut multiple_used = false; - fields.for_each(|(field_id, _data)| { - field_tys[field_id].value.walk(&mut |ty| match ty { - &Ty::Bound(idx) if idx == unsize_generic_index => multiple_used = true, - _ => {} - }) - }); - if multiple_used { - return None; - } - - let unsize_generic_index = unsize_generic_index as usize; - - // Check `Unsize` first - match self.check_unsize_and_coerce( - st1.get(unsize_generic_index)?, - st2.get(unsize_generic_index)?, - depth + 1, - ) { - Some(true) => {} - ret => return ret, - } - - // Then unify other parameters - let ret = st1 - .iter() - .zip(st2.iter()) - .enumerate() - .filter(|&(idx, _)| idx != unsize_generic_index) - .all(|(_, (ty1, ty2))| self.unify(ty1, ty2)); - - Some(ret) - } - - _ => None, - } + Some(true) } /// Unify `from_ty` to `to_ty` with optional auto Deref diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 42330b269f..aa2dfb5f0c 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -548,3 +548,85 @@ impl S { "### ); } + +#[test] +fn coerce_unsize_array() { + assert_snapshot!( + infer_with_mismatches(r#" +#[lang = "unsize"] +pub trait Unsize {} +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl, U> CoerceUnsized<&U> for &T {} + +fn test() { + let f: &[usize] = &[1, 2, 3]; +} +"#, true), + @r###" + [162; 199) '{ ... 3]; }': () + [172; 173) 'f': &[usize] + [186; 196) '&[1, 2, 3]': &[usize; _] + [187; 196) '[1, 2, 3]': [usize; _] + [188; 189) '1': usize + [191; 192) '2': usize + [194; 195) '3': usize + "### + ); +} + +#[ignore] +#[test] +fn coerce_unsize_trait_object() { + assert_snapshot!( + infer_with_mismatches(r#" +#[lang = "unsize"] +pub trait Unsize {} +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl, U> CoerceUnsized<&U> for &T {} + +trait Foo {} +trait Bar: Foo {} +struct S; +impl Foo for S {} +impl Bar for S {} + +fn test() { + let obj: &dyn Bar = &S; + let obj: &dyn Foo = obj; +} +"#, true), + @r###" + "### + ); +} + +#[ignore] +#[test] +fn coerce_unsize_generic() { + // FIXME: Implement this + // https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions + assert_snapshot!( + infer_with_mismatches(r#" +#[lang = "unsize"] +pub trait Unsize {} +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl, U> CoerceUnsized<&U> for &T {} + +struct Foo { t: T }; +struct Bar(Foo); + +fn test() { + let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; + let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); +} +"#, true), + @r###" + "### + ); +} diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index e83449957e..c385f00986 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -343,6 +343,8 @@ pub enum Impl { ImplBlock(ImplId), /// Closure types implement the Fn traits synthetically. ClosureFnTraitImpl(ClosureFnTraitImplData), + /// [T; n]: Unsize<[T]> + UnsizeArray, } /// This exists just for Chalk, because our ImplIds are only unique per module. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index a537420a53..394232fd94 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -5,7 +5,7 @@ use hir_expand::name::name; use ra_db::CrateId; use super::{AssocTyValue, Impl}; -use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor}; +use crate::{db::HirDatabase, utils::generics, ApplicationTy, Substs, TraitRef, Ty, TypeCtor}; pub(super) struct BuiltinImplData { pub num_vars: usize, @@ -43,12 +43,22 @@ pub(super) fn get_builtin_impls( } } } + if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty { + if let Some(actual_trait) = get_unsize_trait(db, krate) { + if trait_ == actual_trait { + if check_unsize_impl_prerequisites(db, krate) { + callback(Impl::UnsizeArray); + } + } + } + } } pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData { match impl_ { Impl::ImplBlock(_) => unreachable!(), Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data), + Impl::UnsizeArray => array_unsize_impl_datum(db, krate), } } @@ -65,6 +75,8 @@ pub(super) fn associated_ty_value( } } +// Closure Fn trait impls + fn check_closure_fn_trait_impl_prerequisites( db: &impl HirDatabase, krate: CrateId, @@ -165,6 +177,45 @@ fn closure_fn_trait_output_assoc_ty_value( } } +// Array unsizing + +fn check_unsize_impl_prerequisites(db: &impl HirDatabase, krate: CrateId) -> bool { + // the Unsize trait needs to exist and have two type parameters (Self and T) + let unsize_trait = match get_unsize_trait(db, krate) { + Some(t) => t, + None => return false, + }; + let generic_params = generics(db, unsize_trait.into()); + if generic_params.len() != 2 { + return false; + } + true +} + +fn array_unsize_impl_datum(db: &impl HirDatabase, krate: CrateId) -> BuiltinImplData { + // impl Unsize<[T]> for [T; _] + // (this can be a single impl because we don't distinguish array sizes currently) + + let trait_ = get_unsize_trait(db, krate) // get unsize trait + // the existence of the Unsize trait has been checked before + .expect("Unsize trait missing"); + + let var = Ty::Bound(0); + let substs = Substs::builder(2) + .push(Ty::apply_one(TypeCtor::Array, var.clone())) + .push(Ty::apply_one(TypeCtor::Slice, var)) + .build(); + + let trait_ref = TraitRef { trait_, substs }; + + BuiltinImplData { + num_vars: 1, + trait_ref, + where_clauses: Vec::new(), + assoc_ty_values: Vec::new(), + } +} + fn get_fn_trait( db: &impl HirDatabase, krate: CrateId, @@ -176,3 +227,11 @@ fn get_fn_trait( _ => None, } } + +fn get_unsize_trait(db: &impl HirDatabase, krate: CrateId) -> Option { + let target = db.lang_item(krate, "unsize".into())?; + match target { + LangItemTarget::TraitId(t) => Some(t), + _ => None, + } +} From 0dfbbaf03b03618dcb7ba203ddc453533bb8d1b4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 Feb 2020 19:05:27 +0100 Subject: [PATCH 2/8] Implement dyn Trait unsizing as well --- crates/ra_hir_ty/src/lib.rs | 11 +++ crates/ra_hir_ty/src/tests/coercion.rs | 8 +- crates/ra_hir_ty/src/traits.rs | 10 +++ crates/ra_hir_ty/src/traits/builtin.rs | 112 +++++++++++++++++++++++-- crates/ra_hir_ty/src/traits/chalk.rs | 4 +- 5 files changed, 136 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 13c5e6c6b7..15356ab37d 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -661,6 +661,17 @@ impl Ty { } } + /// If this is a `dyn Trait` type, this returns the `Trait` part. + pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { + match self { + Ty::Dyn(bounds) => bounds.get(0).and_then(|b| match b { + GenericPredicate::Implemented(trait_ref) => Some(trait_ref), + _ => None, + }), + _ => None, + } + } + fn builtin_deref(&self) -> Option { match self { Ty::Apply(a_ty) => match a_ty.ctor { diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index aa2dfb5f0c..b6fce93772 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -576,7 +576,6 @@ fn test() { ); } -#[ignore] #[test] fn coerce_unsize_trait_object() { assert_snapshot!( @@ -600,6 +599,13 @@ fn test() { } "#, true), @r###" + [240; 300) '{ ...obj; }': () + [250; 253) 'obj': &dyn Bar + [266; 268) '&S': &S + [267; 268) 'S': S + [278; 281) 'obj': &dyn Foo + [294; 297) 'obj': &dyn Bar + [294; 297): expected &dyn Foo, got &dyn Bar "### ); } diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index c385f00986..2317fcac36 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -335,6 +335,12 @@ pub struct ClosureFnTraitImplData { fn_trait: FnTrait, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnsizeToSuperTraitObjectData { + trait_: TraitId, + super_trait: TraitId, +} + /// An impl. Usually this comes from an impl block, but some built-in types get /// synthetic impls. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -345,6 +351,10 @@ pub enum Impl { ClosureFnTraitImpl(ClosureFnTraitImplData), /// [T; n]: Unsize<[T]> UnsizeArray, + /// T: Unsize where T: Trait + UnsizeToTraitObject(TraitId), + /// dyn Trait: Unsize if Trait: SuperTrait + UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData), } /// This exists just for Chalk, because our ImplIds are only unique per module. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index 394232fd94..3c8dd4af86 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -4,8 +4,12 @@ use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId}; use hir_expand::name::name; use ra_db::CrateId; -use super::{AssocTyValue, Impl}; -use crate::{db::HirDatabase, utils::generics, ApplicationTy, Substs, TraitRef, Ty, TypeCtor}; +use super::{AssocTyValue, Impl, UnsizeToSuperTraitObjectData}; +use crate::{ + db::HirDatabase, + utils::{all_super_traits, generics}, + ApplicationTy, GenericPredicate, Substs, TraitRef, Ty, TypeCtor, +}; pub(super) struct BuiltinImplData { pub num_vars: usize, @@ -25,6 +29,8 @@ pub(super) fn get_builtin_impls( db: &impl HirDatabase, krate: CrateId, ty: &Ty, + // The first argument for the trait, if present + arg: &Option, trait_: TraitId, mut callback: impl FnMut(Impl), ) { @@ -43,14 +49,43 @@ pub(super) fn get_builtin_impls( } } } + + let unsize_trait = get_unsize_trait(db, krate); + if let Some(actual_trait) = unsize_trait { + if trait_ == actual_trait { + get_builtin_unsize_impls(db, krate, ty, arg, callback); + } + } +} + +fn get_builtin_unsize_impls( + db: &impl HirDatabase, + krate: CrateId, + ty: &Ty, + // The first argument for the trait, if present + arg: &Option, + mut callback: impl FnMut(Impl), +) { + if !check_unsize_impl_prerequisites(db, krate) { + return; + } + if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty { - if let Some(actual_trait) = get_unsize_trait(db, krate) { - if trait_ == actual_trait { - if check_unsize_impl_prerequisites(db, krate) { - callback(Impl::UnsizeArray); - } + callback(Impl::UnsizeArray); + } + + if let Some(target_trait) = arg.as_ref().and_then(|t| t.dyn_trait_ref()) { + if let Some(trait_ref) = ty.dyn_trait_ref() { + let super_traits = all_super_traits(db, trait_ref.trait_); + if super_traits.contains(&target_trait.trait_) { + // callback(Impl::UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData { + // trait_: trait_ref.trait_, + // super_trait: target_trait.trait_, + // })); } } + + callback(Impl::UnsizeToTraitObject(target_trait.trait_)); } } @@ -59,6 +94,10 @@ pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> Impl::ImplBlock(_) => unreachable!(), Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data), Impl::UnsizeArray => array_unsize_impl_datum(db, krate), + Impl::UnsizeToTraitObject(trait_) => trait_object_unsize_impl_datum(db, krate, trait_), + Impl::UnsizeToSuperTraitObject(data) => { + super_trait_object_unsize_impl_datum(db, krate, data) + } } } @@ -216,6 +255,65 @@ fn array_unsize_impl_datum(db: &impl HirDatabase, krate: CrateId) -> BuiltinImpl } } +// Trait object unsizing + +fn trait_object_unsize_impl_datum( + db: &impl HirDatabase, + krate: CrateId, + trait_: TraitId, +) -> BuiltinImplData { + // impl Unsize> for T where T: Trait + + let unsize_trait = get_unsize_trait(db, krate) // get unsize trait + // the existence of the Unsize trait has been checked before + .expect("Unsize trait missing"); + + let self_ty = Ty::Bound(0); + + let substs = Substs::build_for_def(db, trait_) + // this fits together nicely: $0 is our self type, and the rest are the type + // args for the trait + .fill_with_bound_vars(0) + .build(); + let trait_ref = TraitRef { trait_, substs }; + // This is both the bound for the `dyn` type, *and* the bound for the impl! + // This works because the self type for `dyn` is always Ty::Bound(0), which + // we've also made the parameter for our impl self type. + let bounds = vec![GenericPredicate::Implemented(trait_ref)]; + + let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(bounds.clone().into())).build(); + + let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; + + BuiltinImplData { num_vars: 1, trait_ref, where_clauses: bounds, assoc_ty_values: Vec::new() } +} + +fn super_trait_object_unsize_impl_datum( + db: &impl HirDatabase, + krate: CrateId, + _data: UnsizeToSuperTraitObjectData, +) -> BuiltinImplData { + // impl Unsize for dyn Trait + + let unsize_trait = get_unsize_trait(db, krate) // get unsize trait + // the existence of the Unsize trait has been checked before + .expect("Unsize trait missing"); + + let substs = Substs::builder(2) + // .push(Ty::Dyn(todo!())) + // .push(Ty::Dyn(todo!())) + .build(); + + let trait_ref = TraitRef { trait_: unsize_trait, substs }; + + BuiltinImplData { + num_vars: 1, + trait_ref, + where_clauses: Vec::new(), + assoc_ty_values: Vec::new(), + } +} + fn get_fn_trait( db: &impl HirDatabase, krate: CrateId, diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 1bdf13e480..e1e430aeb0 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -572,8 +572,10 @@ where .collect(); let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone()); + let arg: Option = + parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref().clone())); - builtin::get_builtin_impls(self.db, self.krate, &ty, trait_, |i| { + builtin::get_builtin_impls(self.db, self.krate, &ty, &arg, trait_, |i| { result.push(i.to_chalk(self.db)) }); From f126808b2ee79792631edc377bc8c2b0f329eebf Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 Feb 2020 21:46:21 +0100 Subject: [PATCH 3/8] Fix handling of binders in canonicalization I'm looking forward to getting rid of this in favor of Chalk's implementation. --- crates/ra_hir_ty/src/infer/unify.rs | 99 ++++++++++------------------- 1 file changed, 35 insertions(+), 64 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index 2d03c5c33b..aed527fe59 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -7,10 +7,7 @@ use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; use test_utils::tested_by; use super::{InferenceContext, Obligation}; -use crate::{ - db::HirDatabase, utils::make_mut_slice, Canonical, InEnvironment, InferTy, ProjectionPredicate, - ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, -}; +use crate::{db::HirDatabase, Canonical, InEnvironment, InferTy, Substs, Ty, TypeCtor, TypeWalk}; impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D> @@ -50,42 +47,38 @@ where }) } - fn do_canonicalize_ty(&mut self, ty: Ty) -> Ty { - ty.fold(&mut |ty| match ty { - Ty::Infer(tv) => { - let inner = tv.to_inner(); - if self.var_stack.contains(&inner) { - // recursive type - return tv.fallback_value(); + fn do_canonicalize(&mut self, t: T, binders: usize) -> T { + t.fold_binders( + &mut |ty, binders| match ty { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + if self.var_stack.contains(&inner) { + // recursive type + return tv.fallback_value(); + } + if let Some(known_ty) = + self.ctx.table.var_unification_table.inlined_probe_value(inner).known() + { + self.var_stack.push(inner); + let result = self.do_canonicalize(known_ty.clone(), binders); + self.var_stack.pop(); + result + } else { + let root = self.ctx.table.var_unification_table.find(inner); + let free_var = match tv { + InferTy::TypeVar(_) => InferTy::TypeVar(root), + InferTy::IntVar(_) => InferTy::IntVar(root), + InferTy::FloatVar(_) => InferTy::FloatVar(root), + InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root), + }; + let position = self.add(free_var); + Ty::Bound((position + binders) as u32) + } } - if let Some(known_ty) = - self.ctx.table.var_unification_table.inlined_probe_value(inner).known() - { - self.var_stack.push(inner); - let result = self.do_canonicalize_ty(known_ty.clone()); - self.var_stack.pop(); - result - } else { - let root = self.ctx.table.var_unification_table.find(inner); - let free_var = match tv { - InferTy::TypeVar(_) => InferTy::TypeVar(root), - InferTy::IntVar(_) => InferTy::IntVar(root), - InferTy::FloatVar(_) => InferTy::FloatVar(root), - InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root), - }; - let position = self.add(free_var); - Ty::Bound(position as u32) - } - } - _ => ty, - }) - } - - fn do_canonicalize_trait_ref(&mut self, mut trait_ref: TraitRef) -> TraitRef { - for ty in make_mut_slice(&mut trait_ref.substs.0) { - *ty = self.do_canonicalize_ty(ty.clone()); - } - trait_ref + _ => ty, + }, + binders, + ) } fn into_canonicalized(self, result: T) -> Canonicalized { @@ -95,28 +88,8 @@ where } } - fn do_canonicalize_projection_ty(&mut self, mut projection_ty: ProjectionTy) -> ProjectionTy { - for ty in make_mut_slice(&mut projection_ty.parameters.0) { - *ty = self.do_canonicalize_ty(ty.clone()); - } - projection_ty - } - - fn do_canonicalize_projection_predicate( - &mut self, - projection: ProjectionPredicate, - ) -> ProjectionPredicate { - let ty = self.do_canonicalize_ty(projection.ty); - let projection_ty = self.do_canonicalize_projection_ty(projection.projection_ty); - - ProjectionPredicate { ty, projection_ty } - } - - // FIXME: add some point, we need to introduce a `Fold` trait that abstracts - // over all the things that can be canonicalized (like Chalk and rustc have) - pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized { - let result = self.do_canonicalize_ty(ty); + let result = self.do_canonicalize(ty, 0); self.into_canonicalized(result) } @@ -125,10 +98,8 @@ where obligation: InEnvironment, ) -> Canonicalized> { let result = match obligation.value { - Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize_trait_ref(tr)), - Obligation::Projection(pr) => { - Obligation::Projection(self.do_canonicalize_projection_predicate(pr)) - } + Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize(tr, 0)), + Obligation::Projection(pr) => Obligation::Projection(self.do_canonicalize(pr, 0)), }; self.into_canonicalized(InEnvironment { value: result, From 2d5ab6324795e5fc36e4b61cb66737958dc67e7a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 Feb 2020 21:49:02 +0100 Subject: [PATCH 4/8] Add &dyn Trait -> &dyn SuperTrait coercion, and fix &T -> &dyn Trait --- crates/ra_hir_ty/src/lib.rs | 20 ++++++ crates/ra_hir_ty/src/tests/coercion.rs | 40 +++++++---- crates/ra_hir_ty/src/traits/builtin.rs | 95 ++++++++++++++++++-------- crates/ra_hir_ty/src/utils.rs | 21 ++++++ 4 files changed, 132 insertions(+), 44 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 15356ab37d..2f2d3080ec 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -461,6 +461,12 @@ impl Binders { } } +impl Binders<&T> { + pub fn cloned(&self) -> Binders { + Binders { num_binders: self.num_binders, value: self.value.clone() } + } +} + impl Binders { /// Substitutes all variables. pub fn subst(self, subst: &Substs) -> T { @@ -757,6 +763,20 @@ pub trait TypeWalk { /// variable for the self type. fn walk_mut_binders(&mut self, f: &mut impl FnMut(&mut Ty, usize), binders: usize); + fn fold_binders(mut self, f: &mut impl FnMut(Ty, usize) -> Ty, binders: usize) -> Self + where + Self: Sized, + { + self.walk_mut_binders( + &mut |ty_mut, binders| { + let ty = mem::replace(ty_mut, Ty::Unknown); + *ty_mut = f(ty, binders); + }, + binders, + ); + self + } + fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Self where Self: Sized, diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index b6fce93772..5594ed394f 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -587,25 +587,37 @@ pub trait CoerceUnsized {} impl, U> CoerceUnsized<&U> for &T {} -trait Foo {} -trait Bar: Foo {} -struct S; -impl Foo for S {} -impl Bar for S {} +trait Foo {} +trait Bar: Foo {} +trait Baz: Bar {} + +struct S; +impl Foo for S {} +impl Bar for S {} +impl Baz for S {} fn test() { - let obj: &dyn Bar = &S; - let obj: &dyn Foo = obj; + let obj: &dyn Baz = &S; + let obj: &dyn Bar<_, _, _> = obj; + let obj: &dyn Foo<_, _> = obj; + let obj2: &dyn Baz = &S; + let _: &dyn Foo<_, _> = obj2; } "#, true), @r###" - [240; 300) '{ ...obj; }': () - [250; 253) 'obj': &dyn Bar - [266; 268) '&S': &S - [267; 268) 'S': S - [278; 281) 'obj': &dyn Foo - [294; 297) 'obj': &dyn Bar - [294; 297): expected &dyn Foo, got &dyn Bar + [388; 573) '{ ...bj2; }': () + [398; 401) 'obj': &dyn Baz + [423; 425) '&S': &S + [424; 425) 'S': S + [435; 438) 'obj': &dyn Bar + [460; 463) 'obj': &dyn Baz + [473; 476) 'obj': &dyn Foo + [495; 498) 'obj': &dyn Bar + [508; 512) 'obj2': &dyn Baz + [534; 536) '&S': &S + [535; 536) 'S': S + [546; 547) '_': &dyn Foo + [566; 570) 'obj2': &dyn Baz "### ); } diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index 3c8dd4af86..19e533cee1 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -8,7 +8,7 @@ use super::{AssocTyValue, Impl, UnsizeToSuperTraitObjectData}; use crate::{ db::HirDatabase, utils::{all_super_traits, generics}, - ApplicationTy, GenericPredicate, Substs, TraitRef, Ty, TypeCtor, + ApplicationTy, Binders, GenericPredicate, Substs, TraitRef, Ty, TypeCtor, }; pub(super) struct BuiltinImplData { @@ -72,20 +72,25 @@ fn get_builtin_unsize_impls( if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty { callback(Impl::UnsizeArray); + return; // array is unsized, the rest of the impls shouldn't apply } if let Some(target_trait) = arg.as_ref().and_then(|t| t.dyn_trait_ref()) { + // FIXME what about more complicated dyn tys with marker traits? if let Some(trait_ref) = ty.dyn_trait_ref() { - let super_traits = all_super_traits(db, trait_ref.trait_); - if super_traits.contains(&target_trait.trait_) { - // callback(Impl::UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData { - // trait_: trait_ref.trait_, - // super_trait: target_trait.trait_, - // })); + if trait_ref.trait_ != target_trait.trait_ { + let super_traits = all_super_traits(db, trait_ref.trait_); + if super_traits.contains(&target_trait.trait_) { + callback(Impl::UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData { + trait_: trait_ref.trait_, + super_trait: target_trait.trait_, + })); + } } + } else { + // FIXME only for sized types + callback(Impl::UnsizeToTraitObject(target_trait.trait_)); } - - callback(Impl::UnsizeToTraitObject(target_trait.trait_)); } } @@ -270,48 +275,78 @@ fn trait_object_unsize_impl_datum( let self_ty = Ty::Bound(0); - let substs = Substs::build_for_def(db, trait_) - // this fits together nicely: $0 is our self type, and the rest are the type - // args for the trait - .fill_with_bound_vars(0) + let target_substs = Substs::build_for_def(db, trait_) + .push(Ty::Bound(0)) + // starting from ^2 because we want to start with ^1 outside of the + // `dyn`, which is ^2 inside + .fill_with_bound_vars(2) .build(); - let trait_ref = TraitRef { trait_, substs }; - // This is both the bound for the `dyn` type, *and* the bound for the impl! - // This works because the self type for `dyn` is always Ty::Bound(0), which - // we've also made the parameter for our impl self type. - let bounds = vec![GenericPredicate::Implemented(trait_ref)]; + let num_vars = target_substs.len(); + let target_trait_ref = TraitRef { trait_, substs: target_substs }; + let target_bounds = vec![GenericPredicate::Implemented(target_trait_ref)]; - let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(bounds.clone().into())).build(); + let self_substs = Substs::build_for_def(db, trait_).fill_with_bound_vars(0).build(); + let self_trait_ref = TraitRef { trait_, substs: self_substs }; + let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)]; + + let impl_substs = + Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.clone().into())).build(); let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; - BuiltinImplData { num_vars: 1, trait_ref, where_clauses: bounds, assoc_ty_values: Vec::new() } + BuiltinImplData { num_vars, trait_ref, where_clauses, assoc_ty_values: Vec::new() } } fn super_trait_object_unsize_impl_datum( db: &impl HirDatabase, krate: CrateId, - _data: UnsizeToSuperTraitObjectData, + data: UnsizeToSuperTraitObjectData, ) -> BuiltinImplData { - // impl Unsize for dyn Trait + // impl Unsize for dyn Trait let unsize_trait = get_unsize_trait(db, krate) // get unsize trait // the existence of the Unsize trait has been checked before .expect("Unsize trait missing"); + let self_substs = Substs::build_for_def(db, data.trait_).fill_with_bound_vars(0).build(); + + let num_vars = self_substs.len() - 1; + + let self_trait_ref = TraitRef { trait_: data.trait_, substs: self_substs.clone() }; + let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())]; + + // we need to go from our trait to the super trait, substituting type parameters + let mut path = crate::utils::find_super_trait_path(db, data.super_trait, data.trait_); + path.pop(); // the last one is our current trait, we don't need that + path.reverse(); // we want to go from trait to super trait + + let mut current_trait_ref = self_trait_ref; + for t in path { + let bounds = db.generic_predicates(current_trait_ref.trait_.into()); + let super_trait_ref = bounds + .iter() + .find_map(|b| match &b.value { + GenericPredicate::Implemented(tr) + if tr.trait_ == t && tr.substs[0] == Ty::Bound(0) => + { + Some(Binders { value: tr, num_binders: b.num_binders }) + } + _ => None, + }) + .expect("trait bound for known super trait not found"); + current_trait_ref = super_trait_ref.cloned().subst(¤t_trait_ref.substs); + } + + let super_bounds = vec![GenericPredicate::Implemented(current_trait_ref)]; + let substs = Substs::builder(2) - // .push(Ty::Dyn(todo!())) - // .push(Ty::Dyn(todo!())) + .push(Ty::Dyn(self_bounds.into())) + .push(Ty::Dyn(super_bounds.into())) .build(); let trait_ref = TraitRef { trait_: unsize_trait, substs }; - BuiltinImplData { - num_vars: 1, - trait_ref, - where_clauses: Vec::new(), - assoc_ty_values: Vec::new(), - } + BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } } fn get_fn_trait( diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index 508ae90465..0d1583c39d 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs @@ -62,6 +62,27 @@ pub(super) fn all_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec Vec { + if trait_ == super_trait { + return vec![trait_]; + } + + for tt in direct_super_traits(db, trait_) { + let mut path = find_super_trait_path(db, super_trait, tt); + if !path.is_empty() { + path.push(trait_); + return path; + } + } + Vec::new() +} + pub(super) fn associated_type_by_name_including_super_traits( db: &impl DefDatabase, trait_: TraitId, From 463df6720cc8d2c7176a48a0ca8f4e333016a16a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 Feb 2020 23:06:18 +0100 Subject: [PATCH 5/8] Fix wrong handling of bare `dyn Trait` exposed by canonicalizer fix The self type in the `dyn Trait` trait ref should always be ^0, but we didn't put that in there in the bare case. --- crates/ra_hir_ty/src/lower.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 52da34574c..0e6efa9712 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -239,7 +239,9 @@ impl Ty { ) -> Ty { let ty = match resolution { TypeNs::TraitId(trait_) => { - let trait_ref = TraitRef::from_resolved_path(ctx, trait_, resolved_segment, None); + // if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there + let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None }; + let trait_ref = TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); return if remaining_segments.len() == 1 { let segment = remaining_segments.first().unwrap(); let associated_ty = associated_type_by_name_including_super_traits( From c2000257941956cd4c4365d6eb6cdbc1b16e929c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 21 Feb 2020 23:07:29 +0100 Subject: [PATCH 6/8] Fix shift_bound_vars It should only shift free vars (maybe the name isn't the best...) --- crates/ra_hir_ty/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2f2d3080ec..182f847f17 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -814,13 +814,13 @@ pub trait TypeWalk { where Self: Sized, { - self.fold(&mut |ty| match ty { - Ty::Bound(idx) => { + self.fold_binders(&mut |ty, binders| match ty { + Ty::Bound(idx) if idx as usize >= binders => { assert!(idx as i32 >= -n); Ty::Bound((idx as i32 + n) as u32) } ty => ty, - }) + }, 0) } } From 3e106c77ff76c39be49444165eac805d32666e41 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 22 Feb 2020 13:14:39 +0100 Subject: [PATCH 7/8] Rework find_super_trait_path to protect against cycles --- crates/ra_hir_ty/src/lib.rs | 17 +++++++----- crates/ra_hir_ty/src/lower.rs | 3 +- crates/ra_hir_ty/src/tests/coercion.rs | 38 ++++++++++++++++++++++++++ crates/ra_hir_ty/src/traits/builtin.rs | 6 ++-- crates/ra_hir_ty/src/utils.rs | 33 ++++++++++++++-------- 5 files changed, 74 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 182f847f17..0009c426c4 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -814,13 +814,16 @@ pub trait TypeWalk { where Self: Sized, { - self.fold_binders(&mut |ty, binders| match ty { - Ty::Bound(idx) if idx as usize >= binders => { - assert!(idx as i32 >= -n); - Ty::Bound((idx as i32 + n) as u32) - } - ty => ty, - }, 0) + self.fold_binders( + &mut |ty, binders| match ty { + Ty::Bound(idx) if idx as usize >= binders => { + assert!(idx as i32 >= -n); + Ty::Bound((idx as i32 + n) as u32) + } + ty => ty, + }, + 0, + ) } } diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 0e6efa9712..092977e930 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -241,7 +241,8 @@ impl Ty { TypeNs::TraitId(trait_) => { // if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None }; - let trait_ref = TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); + let trait_ref = + TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); return if remaining_segments.len() == 1 { let segment = remaining_segments.first().unwrap(); let associated_ty = associated_type_by_name_including_super_traits( diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 5594ed394f..60ad6e9be3 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -622,6 +622,44 @@ fn test() { ); } +#[test] +fn coerce_unsize_super_trait_cycle() { + assert_snapshot!( + infer_with_mismatches(r#" +#[lang = "unsize"] +pub trait Unsize {} +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl, U> CoerceUnsized<&U> for &T {} + +trait A {} +trait B: C + A {} +trait C: B {} +trait D: C + +struct S; +impl A for S {} +impl B for S {} +impl C for S {} +impl D for S {} + +fn test() { + let obj: &dyn D = &S; + let obj: &dyn A = obj; +} +"#, true), + @r###" + [292; 348) '{ ...obj; }': () + [302; 305) 'obj': &dyn D + [316; 318) '&S': &S + [317; 318) 'S': S + [328; 331) 'obj': &dyn A + [342; 345) 'obj': &dyn D + "### + ); +} + #[ignore] #[test] fn coerce_unsize_generic() { diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index 19e533cee1..df0645717c 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -316,12 +316,10 @@ fn super_trait_object_unsize_impl_datum( let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())]; // we need to go from our trait to the super trait, substituting type parameters - let mut path = crate::utils::find_super_trait_path(db, data.super_trait, data.trait_); - path.pop(); // the last one is our current trait, we don't need that - path.reverse(); // we want to go from trait to super trait + let path = crate::utils::find_super_trait_path(db, data.trait_, data.super_trait); let mut current_trait_ref = self_trait_ref; - for t in path { + for t in path.into_iter().skip(1) { let bounds = db.generic_predicates(current_trait_ref.trait_.into()); let super_trait_ref = bounds .iter() diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index 0d1583c39d..463fd65b4e 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs @@ -62,25 +62,36 @@ pub(super) fn all_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec Vec { - if trait_ == super_trait { - return vec![trait_]; - } + let mut result = Vec::with_capacity(2); + result.push(trait_); + return if go(db, super_trait, &mut result) { result } else { Vec::new() }; - for tt in direct_super_traits(db, trait_) { - let mut path = find_super_trait_path(db, super_trait, tt); - if !path.is_empty() { - path.push(trait_); - return path; + fn go(db: &impl DefDatabase, super_trait: TraitId, path: &mut Vec) -> bool { + let trait_ = *path.last().unwrap(); + if trait_ == super_trait { + return true; } + + for tt in direct_super_traits(db, trait_) { + if path.contains(&tt) { + continue; + } + path.push(tt); + if go(db, super_trait, path) { + return true; + } else { + path.pop(); + } + } + false } - Vec::new() } pub(super) fn associated_type_by_name_including_super_traits( From 5a6e770f99d1549432c1e8a1abb1aada09ad2590 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 22 Feb 2020 13:15:54 +0100 Subject: [PATCH 8/8] Shorten some code --- crates/ra_hir_ty/src/traits/builtin.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index df0645717c..cc0f3eeb46 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -230,10 +230,7 @@ fn check_unsize_impl_prerequisites(db: &impl HirDatabase, krate: CrateId) -> boo None => return false, }; let generic_params = generics(db, unsize_trait.into()); - if generic_params.len() != 2 { - return false; - } - true + generic_params.len() == 2 } fn array_unsize_impl_datum(db: &impl HirDatabase, krate: CrateId) -> BuiltinImplData {