diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 45164c9e96a..ce9a06fde4b 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -865,7 +865,7 @@ mod foo { #[test] fn method_resolution_where_clause_for_unknown_trait() { - // The blanket impl shouldn't apply because we can't even resolve UnknownTrait + // The blanket impl currently applies because we ignore the unresolved where clause let t = type_at( r#" //- /main.rs @@ -875,7 +875,7 @@ impl<T> Trait for T where T: UnknownTrait {} fn test() { (&S).foo()<|>; } "#, ); - assert_eq!(t, "{unknown}"); + assert_eq!(t, "u128"); } #[test] diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index fbab609251d..c4dc857bc7b 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -291,7 +291,7 @@ impl FnTrait { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ClosureFnTraitImplData { def: DefWithBodyId, expr: ExprId, @@ -300,7 +300,7 @@ pub struct ClosureFnTraitImplData { /// An impl. Usually this comes from an impl block, but some built-in types get /// synthetic impls. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Impl { /// A normal impl from an impl block. ImplBlock(ImplId), diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index cd587a33828..dd41176f0df 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs @@ -28,24 +28,24 @@ pub(super) fn get_builtin_impls( trait_: TraitId, mut callback: impl FnMut(Impl), ) { + // Note: since impl_datum needs to be infallible, we need to make sure here + // that we have all prerequisites to build the respective impls. if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() { if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) { if trait_ == actual_trait { let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait }; - callback(Impl::ClosureFnTraitImpl(impl_)); + if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) { + callback(Impl::ClosureFnTraitImpl(impl_)); + } } } } } } -pub(super) fn impl_datum( - db: &impl HirDatabase, - krate: CrateId, - impl_: Impl, -) -> Option<BuiltinImplData> { +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), @@ -65,21 +65,38 @@ pub(super) fn associated_ty_value( } } +fn check_closure_fn_trait_impl_prerequisites( + db: &impl HirDatabase, + krate: CrateId, + data: super::ClosureFnTraitImplData, +) -> bool { + // the respective Fn/FnOnce/FnMut trait needs to exist + if get_fn_trait(db, krate, data.fn_trait).is_none() { + return false; + } + + // FIXME: there are more assumptions that we should probably check here: + // the traits having no type params, FnOnce being a supertrait + + // the FnOnce trait needs to exist and have an assoc type named Output + let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) { + Some(t) => t, + None => return false, + }; + db.trait_data(fn_once_trait).associated_type_by_name(&name![Output]).is_some() +} + fn closure_fn_trait_impl_datum( db: &impl HirDatabase, krate: CrateId, data: super::ClosureFnTraitImplData, -) -> Option<BuiltinImplData> { +) -> BuiltinImplData { // for some closure |X, Y| -> Z: // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V } - let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait - - // validate FnOnce trait, since we need it in the assoc ty value definition - // and don't want to return a valid value only to find out later that FnOnce - // is broken - let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?; - let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; + let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait + // the existence of the Fn trait has been checked before + .expect("fn trait for closure impl missing"); let num_args: u16 = match &db.body(data.def.into())[data.expr] { Expr::Lambda { args, .. } => args.len() as u16, @@ -107,12 +124,12 @@ fn closure_fn_trait_impl_datum( let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone()); - Some(BuiltinImplData { + BuiltinImplData { num_vars: num_args as usize + 1, trait_ref, where_clauses: Vec::new(), assoc_ty_values: vec![output_ty_id], - }) + } } fn closure_fn_trait_output_assoc_ty_value( diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index d53d3fdebd3..9e38337e560 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -1,5 +1,5 @@ //! Conversion code from/to Chalk. -use std::sync::Arc; +use std::{fmt, sync::Arc}; use log::debug; @@ -17,7 +17,73 @@ use crate::{ ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, }; -pub type TypeFamily = chalk_ir::family::ChalkIr; +#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct TypeFamily {} + +impl chalk_ir::family::TypeFamily for TypeFamily { + type InternedType = Box<chalk_ir::TyData<Self>>; + type InternedLifetime = chalk_ir::LifetimeData<Self>; + type InternedParameter = chalk_ir::ParameterData<Self>; + type DefId = InternId; + + // FIXME: implement these + fn debug_struct_id( + _type_kind_id: chalk_ir::StructId<Self>, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option<fmt::Result> { + None + } + + fn debug_trait_id( + _type_kind_id: chalk_ir::TraitId<Self>, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option<fmt::Result> { + None + } + + fn debug_assoc_type_id( + _id: chalk_ir::AssocTypeId<Self>, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option<fmt::Result> { + None + } + + fn debug_projection( + _projection: &chalk_ir::ProjectionTy<Self>, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option<fmt::Result> { + None + } + + fn intern_ty(ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> { + Box::new(ty) + } + + fn ty_data(ty: &Box<chalk_ir::TyData<Self>>) -> &chalk_ir::TyData<Self> { + ty + } + + fn intern_lifetime(lifetime: chalk_ir::LifetimeData<Self>) -> chalk_ir::LifetimeData<Self> { + lifetime + } + + fn lifetime_data(lifetime: &chalk_ir::LifetimeData<Self>) -> &chalk_ir::LifetimeData<Self> { + lifetime + } + + fn intern_parameter(parameter: chalk_ir::ParameterData<Self>) -> chalk_ir::ParameterData<Self> { + parameter + } + + fn parameter_data(parameter: &chalk_ir::ParameterData<Self>) -> &chalk_ir::ParameterData<Self> { + parameter + } +} + +impl chalk_ir::family::HasTypeFamily for TypeFamily { + type TypeFamily = Self; +} + pub type AssocTypeId = chalk_ir::AssocTypeId<TypeFamily>; pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<TypeFamily>; pub type TraitId = chalk_ir::TraitId<TypeFamily>; @@ -29,9 +95,6 @@ pub type ImplDatum = chalk_rust_ir::ImplDatum<TypeFamily>; pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId; pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<TypeFamily>; -/// This represents a trait whose name we could not resolve. -const UNKNOWN_TRAIT: TraitId = chalk_ir::TraitId(chalk_ir::RawId { index: u32::max_value() }); - pub(super) trait ToChalk { type Chalk; fn to_chalk(self, db: &impl HirDatabase) -> Self::Chalk; @@ -162,11 +225,11 @@ impl ToChalk for hir_def::TraitId { type Chalk = TraitId; fn to_chalk(self, _db: &impl HirDatabase) -> TraitId { - chalk_ir::TraitId(id_to_chalk(self)) + chalk_ir::TraitId(self.as_intern_id()) } fn from_chalk(_db: &impl HirDatabase, trait_id: TraitId) -> hir_def::TraitId { - id_from_chalk(trait_id.0) + InternKey::from_intern_id(trait_id.0) } } @@ -215,11 +278,11 @@ impl ToChalk for TypeAliasId { type Chalk = AssocTypeId; fn to_chalk(self, _db: &impl HirDatabase) -> AssocTypeId { - chalk_ir::AssocTypeId(id_to_chalk(self)) + chalk_ir::AssocTypeId(self.as_intern_id()) } fn from_chalk(_db: &impl HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId { - id_from_chalk(type_alias_id.0) + InternKey::from_intern_id(type_alias_id.0) } } @@ -250,13 +313,7 @@ impl ToChalk for GenericPredicate { }), 0, ), - GenericPredicate::Error => { - let impossible_trait_ref = chalk_ir::TraitRef { - trait_id: UNKNOWN_TRAIT, - parameters: vec![Ty::Unknown.to_chalk(db).cast()], - }; - make_binders(chalk_ir::WhereClause::Implemented(impossible_trait_ref), 0) - } + GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), } } @@ -266,10 +323,6 @@ impl ToChalk for GenericPredicate { ) -> GenericPredicate { match where_clause.value { chalk_ir::WhereClause::Implemented(tr) => { - if tr.trait_id == UNKNOWN_TRAIT { - // FIXME we need an Error enum on the Chalk side to avoid this - return GenericPredicate::Error; - } GenericPredicate::Implemented(from_chalk(db, tr)) } chalk_ir::WhereClause::ProjectionEq(projection_eq) => { @@ -460,9 +513,8 @@ fn convert_where_clauses( let mut result = Vec::with_capacity(generic_predicates.len()); for pred in generic_predicates.iter() { if pred.is_error() { - // HACK: Return just the single predicate (which is always false - // anyway), otherwise Chalk can easily get into slow situations - return vec![pred.clone().subst(substs).to_chalk(db)]; + // skip errored predicates completely + continue; } result.push(pred.clone().subst(substs).to_chalk(db)); } @@ -491,10 +543,11 @@ where parameters: &[Parameter<TypeFamily>], ) -> Vec<ImplId> { debug!("impls_for_trait {:?}", trait_id); - if trait_id == UNKNOWN_TRAIT { - return Vec::new(); - } let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); + + // Note: Since we're using impls_for_trait, only impls where the trait + // can be resolved should ever reach Chalk. `impl_datum` relies on that + // and will panic if the trait can't be resolved. let mut result: Vec<_> = self .db .impls_for_trait(self.krate, trait_.into()) @@ -566,24 +619,6 @@ pub(crate) fn trait_datum_query( trait_id: TraitId, ) -> Arc<TraitDatum> { debug!("trait_datum {:?}", trait_id); - if trait_id == UNKNOWN_TRAIT { - let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses: Vec::new() }; - - let flags = chalk_rust_ir::TraitFlags { - auto: false, - marker: false, - upstream: true, - fundamental: false, - non_enumerable: true, - coinductive: false, - }; - return Arc::new(TraitDatum { - id: trait_id, - binders: make_binders(trait_datum_bound, 1), - flags, - associated_ty_ids: vec![], - }); - } let trait_: hir_def::TraitId = from_chalk(db, trait_id); let trait_data = db.trait_data(trait_); debug!("trait {:?} = {:?}", trait_id, trait_data.name); @@ -653,9 +688,8 @@ pub(crate) fn impl_datum_query( let impl_: Impl = from_chalk(db, impl_id); match impl_ { Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block), - _ => builtin::impl_datum(db, krate, impl_).map(|d| Arc::new(d.to_chalk(db))), + _ => Arc::new(builtin::impl_datum(db, krate, impl_).to_chalk(db)), } - .unwrap_or_else(invalid_impl_datum) } fn impl_block_datum( @@ -663,8 +697,11 @@ fn impl_block_datum( krate: CrateId, chalk_id: ImplId, impl_id: hir_def::ImplId, -) -> Option<Arc<ImplDatum>> { - let trait_ref = db.impl_trait(impl_id)?; +) -> Arc<ImplDatum> { + let trait_ref = db + .impl_trait(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach Chalk + .expect("invalid impl passed to Chalk"); let impl_data = db.impl_data(impl_id); let generic_params = generics(db, impl_id.into()); @@ -716,21 +753,6 @@ fn impl_block_datum( polarity, associated_ty_value_ids, }; - Some(Arc::new(impl_datum)) -} - -fn invalid_impl_datum() -> Arc<ImplDatum> { - let trait_ref = chalk_ir::TraitRef { - trait_id: UNKNOWN_TRAIT, - parameters: vec![chalk_ir::TyData::BoundVar(0).cast().intern().cast()], - }; - let impl_datum_bound = chalk_rust_ir::ImplDatumBound { trait_ref, where_clauses: Vec::new() }; - let impl_datum = ImplDatum { - binders: make_binders(impl_datum_bound, 1), - impl_type: chalk_rust_ir::ImplType::External, - polarity: chalk_rust_ir::Polarity::Positive, - associated_ty_value_ids: Vec::new(), - }; Arc::new(impl_datum) } @@ -786,25 +808,25 @@ fn id_to_chalk<T: InternKey>(salsa_id: T) -> chalk_ir::RawId { impl From<StructId> for crate::TypeCtorId { fn from(struct_id: StructId) -> Self { - id_from_chalk(struct_id.0) + InternKey::from_intern_id(struct_id.0) } } impl From<crate::TypeCtorId> for StructId { fn from(type_ctor_id: crate::TypeCtorId) -> Self { - chalk_ir::StructId(id_to_chalk(type_ctor_id)) + chalk_ir::StructId(type_ctor_id.as_intern_id()) } } impl From<ImplId> for crate::traits::GlobalImplId { fn from(impl_id: ImplId) -> Self { - id_from_chalk(impl_id.0) + InternKey::from_intern_id(impl_id.0) } } impl From<crate::traits::GlobalImplId> for ImplId { fn from(impl_id: crate::traits::GlobalImplId) -> Self { - chalk_ir::ImplId(id_to_chalk(impl_id)) + chalk_ir::ImplId(impl_id.as_intern_id()) } }