From 02962b374ecefd6f2a75956f4fb18806531d1d51 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 4 Mar 2020 23:00:44 +0100 Subject: Implement return position impl trait / opaque type support This is working, but I'm not that happy with how the lowering works. We might need an additional representation between `TypeRef` and `Ty` where names are resolved and `impl Trait` bounds are separated out, but things like inference variables don't exist and `impl Trait` is always represented the same way. Also note that this doesn't implement correct handling of RPIT *inside* the function (which involves turning the `impl Trait`s into variables and creating obligations for them). That intermediate representation might help there as well. --- crates/ra_hir_ty/src/db.rs | 20 +++- crates/ra_hir_ty/src/display.rs | 37 +++++-- crates/ra_hir_ty/src/infer.rs | 23 +---- crates/ra_hir_ty/src/lib.rs | 98 ++++++++++++++++-- crates/ra_hir_ty/src/lower.rs | 143 ++++++++++++++++++++++---- crates/ra_hir_ty/src/tests/traits.rs | 47 ++++++++- crates/ra_hir_ty/src/traits/chalk.rs | 52 ++++++++-- crates/ra_hir_ty/src/traits/chalk/interner.rs | 2 + crates/ra_hir_ty/src/traits/chalk/mapping.rs | 45 +++++++- crates/ra_hir_ty/src/traits/chalk/tls.rs | 5 + 10 files changed, 395 insertions(+), 77 deletions(-) diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index 0a8bb24ac..bf71d38d6 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use hir_def::{ - db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId, - VariantId, + db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId, + TypeParamId, VariantId, }; use ra_arena::map::ArenaMap; use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; @@ -13,8 +13,8 @@ use ra_prof::profile; use crate::{ method_resolution::{CrateImplDefs, TyFingerprint}, traits::{chalk, AssocTyValue, Impl}, - Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, - TyDefId, TypeCtor, ValueTyDefId, + Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, + ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, }; use hir_expand::name::Name; @@ -48,6 +48,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::callable_item_sig)] fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; + #[salsa::invoke(crate::lower::return_type_impl_traits)] + fn return_type_impl_traits( + &self, + def: FunctionId, + ) -> Option>>; + #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] fn generic_predicates_for_param( @@ -80,6 +86,8 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::interned] fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; #[salsa::interned] + fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId; + #[salsa::interned] fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; #[salsa::interned] fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; @@ -142,3 +150,7 @@ fn hir_database_is_object_safe() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct GlobalTypeParamId(salsa::InternId); impl_intern_key!(GlobalTypeParamId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InternedOpaqueTyId(salsa::InternId); +impl_intern_key!(InternedOpaqueTyId); diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index b9c4d2e89..3e63a2415 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -359,6 +359,21 @@ impl HirDisplay for ApplicationTy { write!(f, ">")?; } } + TypeCtor::OpaqueType(opaque_ty_id) => { + let bounds = match opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + let datas = + f.db.return_type_impl_traits(func).expect("impl trait id without data"); + let data = (*datas) + .as_ref() + .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); + data.clone().subst(&self.parameters) + } + }; + write!(f, "impl ")?; + write_bounds_like_dyn_trait(&bounds.value, f)?; + // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution + } TypeCtor::Closure { .. } => { let sig = self.parameters[0].callable_sig(f.db); if let Some(sig) = sig { @@ -427,14 +442,24 @@ impl HirDisplay for Ty { } } Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { - match self { - Ty::Dyn(_) => write!(f, "dyn ")?, - Ty::Opaque(_) => write!(f, "impl ")?, - _ => unreachable!(), - }; + Ty::Dyn(predicates) => { + write!(f, "dyn ")?; write_bounds_like_dyn_trait(predicates, f)?; } + Ty::Opaque(opaque_ty) => { + let bounds = match opaque_ty.opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + let datas = + f.db.return_type_impl_traits(func).expect("impl trait id without data"); + let data = (*datas) + .as_ref() + .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); + data.clone().subst(&opaque_ty.parameters) + } + }; + write!(f, "impl ")?; + write_bounds_like_dyn_trait(&bounds.value, f)?; + } Ty::Unknown => write!(f, "{{unknown}}")?, Ty::Infer(..) => write!(f, "_")?, } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index dc77e88e5..d9bf3c2f0 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -39,8 +39,8 @@ use ra_syntax::SmolStr; use super::{ primitive::{FloatTy, IntTy}, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, - ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, - TraitRef, Ty, TypeCtor, TypeWalk, Uncertain, + ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, + TypeWalk, Uncertain, }; use crate::{ db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, @@ -383,25 +383,6 @@ impl<'a> InferenceContext<'a> { ) -> Ty { match assoc_ty { Some(res_assoc_ty) => { - // FIXME: - // Check if inner_ty is is `impl Trait` and contained input TypeAlias id - // this is a workaround while Chalk assoc type projection doesn't always work yet, - // but once that is fixed I don't think we should keep this - // (we'll probably change how associated types are resolved anyway) - if let Ty::Opaque(ref predicates) = inner_ty { - for p in predicates.iter() { - if let GenericPredicate::Projection(projection) = p { - if projection.projection_ty.associated_ty == res_assoc_ty { - if let ty_app!(_, params) = &projection.ty { - if params.len() == 0 { - return projection.ty.clone(); - } - } - } - } - } - } - let ty = self.table.new_type_var(); let builder = Substs::build_for_def(self.db, res_assoc_ty) .push(inner_ty) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 9fa8d3bdc..135976fcd 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -147,6 +147,12 @@ pub enum TypeCtor { /// an **application type** like `(Iterator::Item)`. AssociatedType(TypeAliasId), + /// This represents a placeholder for an opaque type in situations where we + /// don't know the hidden type (i.e. currently almost always). This is + /// analogous to the `AssociatedType` type constructor. As with that one, + /// these are only produced by Chalk. + OpaqueType(OpaqueTyId), + /// The type of a specific closure. /// /// The closure signature is stored in a `FnPtr` type in the first type @@ -194,6 +200,14 @@ impl TypeCtor { let generic_params = generics(db.upcast(), type_alias.into()); generic_params.len() } + TypeCtor::OpaqueType(opaque_ty_id) => { + match opaque_ty_id { + OpaqueTyId::ReturnTypeImplTrait(func, _) => { + let generic_params = generics(db.upcast(), func.into()); + generic_params.len() + } + } + } TypeCtor::FnPtr { num_args } => num_args as usize + 1, TypeCtor::Tuple { cardinality } => cardinality as usize, } @@ -220,6 +234,11 @@ impl TypeCtor { TypeCtor::AssociatedType(type_alias) => { Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) } + TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { + OpaqueTyId::ReturnTypeImplTrait(func, _) => { + Some(func.lookup(db.upcast()).module(db.upcast()).krate) + } + }, } } @@ -241,6 +260,7 @@ impl TypeCtor { TypeCtor::Adt(adt) => Some(adt.into()), TypeCtor::FnDef(callable) => Some(callable.into()), TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), + TypeCtor::OpaqueType(_impl_trait_id) => None, } } } @@ -254,6 +274,12 @@ pub struct ApplicationTy { pub parameters: Substs, } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct OpaqueTy { + pub opaque_ty_id: OpaqueTyId, + pub parameters: Substs, +} + /// A "projection" type corresponds to an (unnormalized) /// projection like `>::Foo`. Note that the /// trait and all its parameters are fully known. @@ -308,6 +334,12 @@ pub enum Ty { /// trait and all its parameters are fully known. Projection(ProjectionTy), + /// An opaque type (`impl Trait`). + /// + /// This is currently only used for return type impl trait; each instance of + /// `impl Trait` in a return type gets its own ID. + Opaque(OpaqueTy), + /// A placeholder for a type parameter; for example, `T` in `fn f(x: T) /// {}` when we're type-checking the body of that function. In this /// situation, we know this stands for *some* type, but don't know the exact @@ -332,12 +364,6 @@ pub enum Ty { /// didn't seem worth the overhead yet. Dyn(Arc<[GenericPredicate]>), - /// An opaque type (`impl Trait`). - /// - /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for - /// more. - Opaque(Arc<[GenericPredicate]>), - /// A placeholder for a type which could not be computed; this is propagated /// to avoid useless error messages. Doubles as a placeholder where type /// variables are inserted before type checking, since we want to try to @@ -490,7 +516,7 @@ impl Deref for Substs { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct Binders { pub num_binders: usize, pub value: T, @@ -534,6 +560,20 @@ impl Binders { } } +impl TypeWalk for Binders { + fn walk(&self, f: &mut impl FnMut(&Ty)) { + self.value.walk(f); + } + + fn walk_mut_binders( + &mut self, + f: &mut impl FnMut(&mut Ty, DebruijnIndex), + binders: DebruijnIndex, + ) { + self.value.walk_mut_binders(f, binders.shifted_in()) + } +} + /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. /// Name to be bikeshedded: TraitBound? TraitImplements? #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -947,11 +987,16 @@ impl TypeWalk for Ty { t.walk(f); } } - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + Ty::Dyn(predicates) => { for p in predicates.iter() { p.walk(f); } } + Ty::Opaque(o_ty) => { + for t in o_ty.parameters.iter() { + t.walk(f); + } + } Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); @@ -969,13 +1014,48 @@ impl TypeWalk for Ty { Ty::Projection(p_ty) => { p_ty.parameters.walk_mut_binders(f, binders); } - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + Ty::Dyn(predicates) => { for p in make_mut_slice(predicates) { p.walk_mut_binders(f, binders.shifted_in()); } } + Ty::Opaque(o_ty) => { + o_ty.parameters.walk_mut_binders(f, binders); + } Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self, binders); } } + +impl TypeWalk for Vec { + fn walk(&self, f: &mut impl FnMut(&Ty)) { + for t in self { + t.walk(f); + } + } + fn walk_mut_binders( + &mut self, + f: &mut impl FnMut(&mut Ty, DebruijnIndex), + binders: DebruijnIndex, + ) { + for t in self { + t.walk_mut_binders(f, binders); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum OpaqueTyId { + ReturnTypeImplTrait(hir_def::FunctionId, u16), +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct ReturnTypeImplTraits { + pub(crate) impl_traits: Vec, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct ReturnTypeImplTrait { + pub(crate) bounds: Binders>, +} diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 35ac86a46..dfc018b0b 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -31,8 +31,9 @@ use crate::{ all_super_trait_refs, associated_type_by_name_including_super_traits, generics, make_mut_slice, variant_data, }, - Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, - ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, + Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig, + ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs, + TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, }; use hir_expand::name::Name; @@ -47,7 +48,16 @@ pub struct TyLoweringContext<'a> { /// possible currently, so this should be fine for now. pub type_param_mode: TypeParamLoweringMode, pub impl_trait_mode: ImplTraitLoweringMode, - pub impl_trait_counter: std::cell::Cell, + impl_trait_counter: std::cell::Cell, + /// When turning `impl Trait` into opaque types, we have to collect the + /// bounds at the same time to get the IDs correct (without becoming too + /// complicated). I don't like using interior mutability (as for the + /// counter), but I've tried and failed to make the lifetimes work for + /// passing around a `&mut TyLoweringContext`. The core problem is that + /// we're grouping the mutable data (the counter and this field) together + /// with the immutable context (the references to the DB and resolver). + /// Splitting this up would be a possible fix. + opaque_type_data: std::cell::RefCell>, } impl<'a> TyLoweringContext<'a> { @@ -56,26 +66,42 @@ impl<'a> TyLoweringContext<'a> { let impl_trait_mode = ImplTraitLoweringMode::Disallowed; let type_param_mode = TypeParamLoweringMode::Placeholder; let in_binders = DebruijnIndex::INNERMOST; - Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } + let opaque_type_data = std::cell::RefCell::new(Vec::new()); + Self { + db, + resolver, + in_binders, + impl_trait_mode, + impl_trait_counter, + type_param_mode, + opaque_type_data, + } } - pub fn with_shifted_in( + pub fn with_debruijn( &self, debruijn: DebruijnIndex, f: impl FnOnce(&TyLoweringContext) -> T, ) -> T { + let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); let new_ctx = Self { - in_binders: self.in_binders.shifted_in_from(debruijn), + in_binders: debruijn, impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), + opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), ..*self }; let result = f(&new_ctx); self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); + self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); result } - pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { - Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } + pub fn with_shifted_in( + &self, + debruijn: DebruijnIndex, + f: impl FnOnce(&TyLoweringContext) -> T, + ) -> T { + self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f) } pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { @@ -167,20 +193,47 @@ impl Ty { TypeRef::ImplTrait(bounds) => { match ctx.impl_trait_mode { ImplTraitLoweringMode::Opaque => { - let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); - 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) + let idx = ctx.impl_trait_counter.get(); + ctx.impl_trait_counter.set(idx + 1); + + assert!(idx as usize == ctx.opaque_type_data.borrow().len()); + // this dance is to make sure the data is in the right + // place even if we encounter more opaque types while + // lowering the bounds + ctx.opaque_type_data + .borrow_mut() + .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) }); + // We don't want to lower the bounds inside the binders + // we're currently in, because they don't end up inside + // those binders. E.g. when we have `impl Trait>`, the `impl OtherTrait` can't refer + // to the self parameter from `impl Trait`, and the + // bounds aren't actually stored nested within each + // other, but separately. So if the `T` refers to a type + // parameter of the outer function, it's just one binder + // away instead of two. + let actual_opaque_type_data = ctx + .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { + ReturnTypeImplTrait::from_hir(ctx, &bounds) + }); + ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; + + let func = match ctx.resolver.generic_def() { + Some(GenericDefId::FunctionId(f)) => f, + _ => { + // this shouldn't happen + return (Ty::Unknown, None); + } + }; + let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx); + let generics = generics(ctx.db.upcast(), func.into()); + let parameters = Substs::bound_vars(&generics, ctx.in_binders); + Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) } ImplTraitLoweringMode::Param => { let idx = ctx.impl_trait_counter.get(); - ctx.impl_trait_counter.set(idx + 1); + // FIXME we're probably doing something wrong here + ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16); if let Some(def) = ctx.resolver.generic_def() { let generics = generics(ctx.db.upcast(), def); let param = generics @@ -197,7 +250,8 @@ impl Ty { } ImplTraitLoweringMode::Variable => { let idx = ctx.impl_trait_counter.get(); - ctx.impl_trait_counter.set(idx + 1); + // FIXME we're probably doing something wrong here + ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16); let (parent_params, self_params, list_params, _impl_trait_params) = if let Some(def) = ctx.resolver.generic_def() { let generics = generics(ctx.db.upcast(), def); @@ -663,6 +717,29 @@ fn assoc_type_bindings_from_type_bound<'a>( }) } +impl ReturnTypeImplTrait { + fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { + let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); + let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { + bounds + .iter() + .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) + .collect() + }); + ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } + } +} + +fn count_impl_traits(type_ref: &TypeRef) -> usize { + let mut count = 0; + type_ref.walk(&mut |type_ref| { + if matches!(type_ref, TypeRef::ImplTrait(_)) { + count += 1; + } + }); + count +} + /// Build the signature of a callable item (function, struct or enum variant). pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { match def { @@ -864,7 +941,9 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { .with_impl_trait_mode(ImplTraitLoweringMode::Variable) .with_type_param_mode(TypeParamLoweringMode::Variable); let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::>(); - let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let ctx_ret = TyLoweringContext::new(db, &resolver) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(TypeParamLoweringMode::Variable); let ret = Ty::from_hir(&ctx_ret, &data.ret_type); let generics = generics(db.upcast(), def.into()); let num_binders = generics.len(); @@ -1084,3 +1163,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, )) } + +pub(crate) fn return_type_impl_traits( + db: &impl HirDatabase, + def: hir_def::FunctionId, +) -> Option>> { + // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe + let data = db.function_data(def); + let resolver = def.resolver(db.upcast()); + let ctx_ret = TyLoweringContext::new(db, &resolver) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(TypeParamLoweringMode::Variable); + let _ret = Ty::from_hir(&ctx_ret, &data.ret_type); + let generics = generics(db.upcast(), def.into()); + let num_binders = generics.len(); + let return_type_impl_traits = + ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; + if return_type_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(Binders::new(num_binders, return_type_impl_traits))) + } +} diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index e8778d419..d83dc6d79 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1110,7 +1110,6 @@ fn test() { } #[test] -#[ignore] fn impl_trait() { assert_snapshot!( infer(r#" @@ -1160,6 +1159,52 @@ fn test(x: impl Trait, y: &impl Trait) { ); } +#[test] +fn return_pos_impl_trait() { + assert_snapshot!( + infer(r#" +trait Iterator { + type Item; + fn next(&mut self) -> Self::Item; +} +trait Trait { + fn foo(&self) -> T; +} +fn bar() -> (impl Iterator>, impl Trait) { loop {} } +fn baz(t: T) -> (impl Iterator>, impl Trait) { loop {} } + +fn test() { + // let (a, b) = bar(); + // a.next().foo(); + // b.foo(); + let (c, d) = baz(1u128); + c.next();//.foo(); + // d.foo(); +} +"#), + @r###" + 50..54 'self': &mut Self + 102..106 'self': &Self + 185..196 '{ loop {} }': ({unknown}, {unknown}) + 187..194 'loop {}': ! + 192..194 '{}': () + 207..208 't': T + 269..280 '{ loop {} }': ({unknown}, {unknown}) + 271..278 'loop {}': ! + 276..278 '{}': () + 292..429 '{ ...o(); }': () + 368..374 '(c, d)': (impl Iterator>, impl Trait) + 369..370 'c': impl Iterator> + 372..373 'd': impl Trait + 377..380 'baz': fn baz(u128) -> (impl Iterator>, impl Trait) + 377..387 'baz(1u128)': (impl Iterator>, impl Trait) + 381..386 '1u128': u128 + 393..394 'c': impl Iterator> + 393..401 'c.next()': impl Trait + "### + ); +} + #[test] fn dyn_trait() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 61de3cc30..a72a82f5a 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use log::debug; use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; -use chalk_solve::rust_ir::{self, WellKnownTrait}; +use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use hir_def::{ lang_item::{lang_attr, LangItemTarget}, @@ -100,6 +100,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc { self.db.associated_ty_value(self.krate, id) } + fn custom_clauses(&self) -> Vec> { vec![] } @@ -130,11 +131,34 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) } - fn opaque_ty_data( - &self, - _id: chalk_ir::OpaqueTyId, - ) -> Arc> { - unimplemented!() + fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { + let interned_id = crate::db::InternedOpaqueTyId::from(id); + let full_id = self.db.lookup_intern_impl_trait_id(interned_id); + let (func, idx) = match full_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx), + }; + let datas = + self.db.return_type_impl_traits(func).expect("impl trait id without impl traits"); + let data = &datas.value.impl_traits[idx as usize]; + let bound = OpaqueTyDatumBound { + bounds: make_binders( + data.bounds + .value + .iter() + .cloned() + .filter(|b| !b.is_error()) + .map(|b| b.to_chalk(self.db)) + .collect(), + 1, + ), + }; + let num_vars = datas.num_binders; + Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) }) + } + + fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { + // FIXME: actually provide the hidden type; it is relevant for auto traits + Ty::Unknown.to_chalk(self.db) } fn force_impl_for( @@ -150,10 +174,6 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { // FIXME: implement actual object safety true } - - fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { - Ty::Unknown.to_chalk(self.db) - } } pub(crate) fn program_clauses_for_chalk_env_query( @@ -460,6 +480,18 @@ impl From for ImplId { } } +impl From for crate::db::InternedOpaqueTyId { + fn from(id: OpaqueTyId) -> Self { + InternKey::from_intern_id(id.0) + } +} + +impl From for OpaqueTyId { + fn from(id: crate::db::InternedOpaqueTyId) -> Self { + chalk_ir::OpaqueTyId(id.as_intern_id()) + } +} + impl From> for crate::traits::AssocTyValueId { fn from(id: rust_ir::AssociatedTyValueId) -> Self { Self::from_intern_id(id.0) diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs index e27074ba6..56aab640c 100644 --- a/crates/ra_hir_ty/src/traits/chalk/interner.rs +++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs @@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId; pub type FnDefId = chalk_ir::FnDefId; pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; +pub type OpaqueTyId = chalk_ir::OpaqueTyId; +pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum; impl chalk_ir::interner::Interner for Interner { type InternedType = Box>; // FIXME use Arc? diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 5f6daf842..834360430 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs @@ -16,8 +16,8 @@ use crate::{ db::HirDatabase, primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, - ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, - Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, + ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, + ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, }; use super::interner::*; @@ -68,7 +68,16 @@ impl ToChalk for Ty { let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) } - Ty::Opaque(_) | Ty::Unknown => { + Ty::Opaque(opaque_ty) => { + let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db); + let substitution = opaque_ty.parameters.to_chalk(db); + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id, + substitution, + })) + .intern(&Interner) + } + Ty::Unknown => { let substitution = chalk_ir::Substitution::empty(&Interner); let name = TypeName::Error; chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) @@ -98,7 +107,11 @@ impl ToChalk for Ty { let parameters = from_chalk(db, proj.substitution); Ty::Projection(ProjectionTy { associated_ty, parameters }) } - chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => { + let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id); + let parameters = from_chalk(db, opaque_ty.substitution); + Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) + } chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { let parameters: Substs = from_chalk(db, substitution); Ty::Apply(ApplicationTy { @@ -204,6 +217,21 @@ impl ToChalk for hir_def::TraitId { } } +impl ToChalk for OpaqueTyId { + type Chalk = chalk_ir::OpaqueTyId; + + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId { + db.intern_impl_trait_id(self).into() + } + + fn from_chalk( + db: &dyn HirDatabase, + opaque_ty_id: chalk_ir::OpaqueTyId, + ) -> OpaqueTyId { + db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + } +} + impl ToChalk for TypeCtor { type Chalk = TypeName; @@ -214,6 +242,11 @@ impl ToChalk for TypeCtor { TypeName::AssociatedType(type_id) } + TypeCtor::OpaqueType(impl_trait_id) => { + let id = impl_trait_id.to_chalk(db); + TypeName::OpaqueType(id) + } + TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), TypeCtor::Char => TypeName::Scalar(Scalar::Char), TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), @@ -252,7 +285,9 @@ impl ToChalk for TypeCtor { match type_name { TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), - TypeName::OpaqueType(_) => unreachable!(), + TypeName::OpaqueType(opaque_type_id) => { + TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) + } TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, TypeName::Scalar(Scalar::Char) => TypeCtor::Char, diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs index d88828c7c..556af7098 100644 --- a/crates/ra_hir_ty/src/traits/chalk/tls.rs +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs @@ -69,6 +69,11 @@ impl DebugContext<'_> { let name = self.0.type_alias_data(type_alias).name.clone(); write!(f, "{}::{}", trait_name, name)?; } + TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { + crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + write!(f, "{{impl trait {} of {:?}}}", idx, func)?; + } + }, TypeCtor::Closure { def, expr } => { write!(f, "{{closure {:?} in ", expr.into_raw())?; match def { -- cgit v1.2.3