From d3cb9ea0bfb6c9e6c8b57a46feb1de696084d994 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 17 Apr 2020 22:48:29 +0200 Subject: Fix another crash from wrong binders Basically, if we had something like `dyn Trait` (where `T` is a type parameter) in an impl we lowered that to `dyn Trait<^0.0>`, when it should be `dyn Trait<^1.0>` because the `dyn` introduces a new binder. With one type parameter, that's just wrong, with two, it'll lead to crashes. --- crates/ra_hir_ty/src/lib.rs | 4 +-- crates/ra_hir_ty/src/lower.rs | 69 +++++++++++++++++++++++++----------- crates/ra_hir_ty/src/tests/traits.rs | 36 +++++++++++++++++++ crates/ra_hir_ty/src/traits/chalk.rs | 10 +++--- 4 files changed, 92 insertions(+), 27 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2677f3af2..a4b8d6683 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -396,12 +396,12 @@ impl Substs { } /// Return Substs that replace each parameter by a bound variable. - pub(crate) fn bound_vars(generic_params: &Generics) -> Substs { + pub(crate) fn bound_vars(generic_params: &Generics, debruijn: DebruijnIndex) -> Substs { Substs( generic_params .iter() .enumerate() - .map(|(idx, _)| Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx))) + .map(|(idx, _)| Ty::Bound(BoundVar::new(debruijn, idx))) .collect(), ) } diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index cc1ac8e3e..c2812e178 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -39,6 +39,7 @@ use crate::{ pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, pub resolver: &'a Resolver, + in_binders: DebruijnIndex, /// Note: Conceptually, it's thinkable that we could be in a location where /// some type params should be represented as placeholders, and others /// should be converted to variables. I think in practice, this isn't @@ -53,7 +54,27 @@ impl<'a> TyLoweringContext<'a> { let impl_trait_counter = std::cell::Cell::new(0); let impl_trait_mode = ImplTraitLoweringMode::Disallowed; let type_param_mode = TypeParamLoweringMode::Placeholder; - Self { db, resolver, impl_trait_mode, impl_trait_counter, type_param_mode } + let in_binders = DebruijnIndex::INNERMOST; + Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } + } + + pub fn with_shifted_in( + &self, + debruijn: DebruijnIndex, + f: impl FnOnce(&TyLoweringContext) -> T, + ) -> T { + let new_ctx = Self { + in_binders: self.in_binders.shifted_in_from(debruijn), + impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), + ..*self + }; + let result = f(&new_ctx); + self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); + result + } + + pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { + Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } } pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { @@ -134,22 +155,26 @@ impl Ty { } TypeRef::DynTrait(bounds) => { let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); - let predicates = bounds - .iter() - .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) - .collect(); + let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { + bounds + .iter() + .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) + .collect() + }); Ty::Dyn(predicates) } TypeRef::ImplTrait(bounds) => { match ctx.impl_trait_mode { ImplTraitLoweringMode::Opaque => { let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); - let predicates = bounds - .iter() - .flat_map(|b| { - GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) - }) - .collect(); + let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { + bounds + .iter() + .flat_map(|b| { + GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) + }) + .collect() + }); Ty::Opaque(predicates) } ImplTraitLoweringMode::Param => { @@ -180,7 +205,7 @@ impl Ty { (0, 0, 0, 0) }; Ty::Bound(BoundVar::new( - DebruijnIndex::INNERMOST, + ctx.in_binders, idx as usize + parent_params + self_params + list_params, )) } @@ -293,7 +318,7 @@ impl Ty { TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id), TypeParamLoweringMode::Variable => { let idx = generics.param_idx(param_id).expect("matching generics"); - Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx)) + Ty::Bound(BoundVar::new(ctx.in_binders, idx)) } } } @@ -303,7 +328,9 @@ impl Ty { TypeParamLoweringMode::Placeholder => { Substs::type_params_for_generics(&generics) } - TypeParamLoweringMode::Variable => Substs::bound_vars(&generics), + TypeParamLoweringMode::Variable => { + Substs::bound_vars(&generics, ctx.in_binders) + } }; ctx.db.impl_self_ty(impl_id).subst(&substs) } @@ -313,7 +340,9 @@ impl Ty { TypeParamLoweringMode::Placeholder => { Substs::type_params_for_generics(&generics) } - TypeParamLoweringMode::Variable => Substs::bound_vars(&generics), + TypeParamLoweringMode::Variable => { + Substs::bound_vars(&generics, ctx.in_binders) + } }; ctx.db.ty(adt.into()).subst(&substs) } @@ -797,7 +826,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { /// function body. fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders { let generics = generics(db.upcast(), def.into()); - let substs = Substs::bound_vars(&generics); + let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) } @@ -851,7 +880,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders Binders { let generics = generics(db.upcast(), adt.into()); - let substs = Substs::bound_vars(&generics); + let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs)) } @@ -892,7 +921,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let ctx = TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); let type_ref = &db.type_alias_data(t).type_ref; - let substs = Substs::bound_vars(&generics); + let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); Binders::new(substs.len(), inner) } diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 0a889f805..36f53b264 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1210,6 +1210,42 @@ fn test(x: dyn Trait, y: &dyn Trait) { ); } +#[test] +fn dyn_trait_in_impl() { + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> (T, U); +} +struct S {} +impl S { + fn bar(&self) -> &dyn Trait { loop {} } +} +trait Trait2 { + fn baz(&self) -> (T, U); +} +impl Trait2 for dyn Trait { } + +fn test(s: S) { + s.bar().baz(); +} +"#), + @r###" + [33; 37) 'self': &Self + [103; 107) 'self': &S + [129; 140) '{ loop {} }': &dyn Trait + [131; 138) 'loop {}': ! + [136; 138) '{}': () + [176; 180) 'self': &Self + [252; 253) 's': S + [268; 290) '{ ...z(); }': () + [274; 275) 's': S + [274; 281) 's.bar()': &dyn Trait + [274; 287) 's.bar().baz()': (u32, i32) + "### + ); +} + #[test] fn dyn_trait_bare() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 60d70d18e..e00a82db2 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -17,7 +17,7 @@ use ra_db::{ use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; use crate::{ db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, - ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, + ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; pub(super) mod tls; @@ -815,7 +815,7 @@ pub(crate) fn associated_ty_data_query( // Lower bounds -- we could/should maybe move this to a separate query in `lower` let type_alias_data = db.type_alias_data(type_alias); let generic_params = generics(db.upcast(), type_alias.into()); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); let ctx = crate::TyLoweringContext::new(db, &resolver) .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); @@ -849,7 +849,7 @@ pub(crate) fn trait_datum_query( let trait_data = db.trait_data(trait_); debug!("trait {:?} = {:?}", trait_id, trait_data.name); let generic_params = generics(db.upcast(), trait_.into()); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); let flags = chalk_rust_ir::TraitFlags { auto: trait_data.auto, upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, @@ -888,7 +888,7 @@ pub(crate) fn struct_datum_query( .as_generic_def() .map(|generic_def| { let generic_params = generics(db.upcast(), generic_def); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); convert_where_clauses(db, generic_def, &bound_vars) }) .unwrap_or_else(Vec::new); @@ -934,7 +934,7 @@ fn impl_def_datum( let impl_data = db.impl_data(impl_id); let generic_params = generics(db.upcast(), impl_id.into()); - let bound_vars = Substs::bound_vars(&generic_params); + let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); let trait_ = trait_ref.trait_; let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { chalk_rust_ir::ImplType::Local -- cgit v1.2.3