From 50bbf9eb09dc34781cc34e10bfba5f154e833123 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 5 May 2019 14:21:00 +0200 Subject: Handle where clauses in trait solving --- crates/ra_hir/src/ty/lower.rs | 53 +++++++++++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 41 ++++++++++++++++++++-- crates/ra_hir/src/ty/traits/chalk.rs | 68 +++++++++++++++++++++++++++++------- 3 files changed, 138 insertions(+), 24 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 8bab7e54b..09d26ce5a 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -5,6 +5,7 @@ //! - Building the type for an item: This happens through the `type_for_def` query. //! //! This usually involves resolving names, collecting generic arguments etc. +use std::sync::Arc; use std::iter; use crate::{ @@ -18,9 +19,9 @@ use crate::{ resolve::{Resolver, Resolution}, path::{PathSegment, GenericArg}, generics::{GenericParams, HasGenericParams}, - adt::VariantDef, Trait + adt::VariantDef, Trait, generics::{ WherePredicate, GenericDef} }; -use super::{Ty, primitive, FnSig, Substs, TypeCtor, TraitRef}; +use super::{Ty, primitive, FnSig, Substs, TypeCtor, TraitRef, GenericPredicate}; impl Ty { pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self { @@ -208,16 +209,12 @@ pub(super) fn substs_from_path_segment( } impl TraitRef { - pub(crate) fn from_hir( + pub(crate) fn from_path( db: &impl HirDatabase, resolver: &Resolver, - type_ref: &TypeRef, + path: &Path, explicit_self_ty: Option, ) -> Option { - let path = match type_ref { - TypeRef::Path(path) => path, - _ => return None, - }; let resolved = match resolver.resolve_path(db, &path).take_types()? { Resolution::Def(ModuleDef::Trait(tr)) => tr, _ => return None, @@ -232,6 +229,19 @@ impl TraitRef { Some(TraitRef { trait_: resolved, substs }) } + pub(crate) fn from_hir( + db: &impl HirDatabase, + resolver: &Resolver, + type_ref: &TypeRef, + explicit_self_ty: Option, + ) -> Option { + let path = match type_ref { + TypeRef::Path(path) => path, + _ => return None, + }; + TraitRef::from_path(db, resolver, path, explicit_self_ty) + } + fn substs_from_path( db: &impl HirDatabase, resolver: &Resolver, @@ -246,6 +256,15 @@ impl TraitRef { let substs = Substs::identity(&trait_.generic_params(db)); TraitRef { trait_, substs } } + + pub(crate) fn for_where_predicate( + db: &impl HirDatabase, + resolver: &Resolver, + pred: &WherePredicate, + ) -> Option { + let self_ty = Ty::from_hir(db, resolver, &pred.type_ref); + TraitRef::from_path(db, resolver, &pred.trait_ref, Some(self_ty)) + } } /// Build the declared type of an item. This depends on the namespace; e.g. for @@ -294,6 +313,24 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { Ty::from_hir(db, &resolver, type_ref) } +/// Resolve the where clause(s) of an item with generics. +pub(crate) fn generic_predicates( + db: &impl HirDatabase, + def: GenericDef, +) -> Arc<[GenericPredicate]> { + let resolver = def.resolver(db); + let generic_params = def.generic_params(db); + let predicates = generic_params + .where_predicates + .iter() + .map(|pred| { + TraitRef::for_where_predicate(db, &resolver, pred) + .map_or(GenericPredicate::Error, GenericPredicate::Implemented) + }) + .collect::>(); + predicates.into() +} + fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig { let signature = def.signature(db); let resolver = def.resolver(db); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index a38fe35c7..c3edf42b1 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2510,12 +2510,47 @@ fn method_resolution_where_clause_not_met() { trait Clone {} trait Trait { fn foo(self) -> u128; } struct S; -impl S { fn foo(self) -> i8 { 0 } } -impl Trait for T where T: Clone { fn foo(self) -> u128 { 0 } } +impl Trait for T where T: Clone {} fn test() { (&S).foo()<|>; } "#, ); - assert_eq!(t, "i8"); + // This is also to make sure that we don't resolve to the foo method just + // because that's the only method named foo we can find, which would make + // the below tests not work + assert_eq!(t, "{unknown}"); +} + +#[test] +fn method_resolution_where_clause_1() { + let t = type_at( + r#" +//- /main.rs +trait Clone {} +trait Trait { fn foo(self) -> u128; } +struct S; +impl Clone for S {}; +impl Trait for T where T: Clone {} +fn test() { S.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn method_resolution_where_clause_2() { + let t = type_at( + r#" +//- /main.rs +trait Into { fn into(self) -> T; } +trait From { fn from(other: T) -> Self; } +struct S1; +struct S2; +impl From for S1 {}; +impl Into for T where U: From {} +fn test() { S2.into()<|>; } +"#, + ); + assert_eq!(t, "S1"); } fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 8b77d21b4..2772a0432 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -11,7 +11,7 @@ use ra_db::salsa::{InternId, InternKey}; use crate::{ Trait, HasGenericParams, ImplBlock, db::HirDatabase, - ty::{TraitRef, Ty, ApplicationTy, TypeCtor, Substs}, + ty::{TraitRef, Ty, ApplicationTy, TypeCtor, Substs, GenericPredicate}, generics::GenericDef, }; use super::ChalkContext; @@ -146,6 +146,27 @@ impl ToChalk for ImplBlock { } } +impl ToChalk for GenericPredicate { + type Chalk = chalk_ir::QuantifiedWhereClause; + + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause { + match self { + GenericPredicate::Implemented(trait_ref) => { + make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0) + } + GenericPredicate::Error => panic!("Trying to pass errored where clause to Chalk"), + } + } + + fn from_chalk( + _db: &impl HirDatabase, + _where_clause: chalk_ir::QuantifiedWhereClause, + ) -> GenericPredicate { + // This should never need to be called + unimplemented!() + } +} + fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders { chalk_ir::Binders { value, @@ -153,6 +174,25 @@ fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders { } } +fn convert_where_clauses( + db: &impl HirDatabase, + def: GenericDef, + substs: &Substs, +) -> (Vec, bool) { + let generic_predicates = db.generic_predicates(def); + let mut result = Vec::with_capacity(generic_predicates.len()); + let mut has_error = false; + for pred in generic_predicates.iter() { + // FIXME: it would probably be nicer if we could just convert errored predicates to a where clause that is never true... + if pred.is_error() { + has_error = true; + } else { + result.push(pred.clone().subst(substs).to_chalk(db)); + } + } + (result, has_error) +} + impl<'a, DB> chalk_solve::RustIrDatabase for ChalkContext<'a, DB> where DB: HirDatabase, @@ -173,7 +213,7 @@ where upstream: trait_.module(self.db).krate(self.db) != Some(self.krate), fundamental: false, }; - let where_clauses = Vec::new(); // FIXME add where clauses + let (where_clauses, _) = convert_where_clauses(self.db, trait_.into(), &bound_vars); let associated_ty_ids = Vec::new(); // FIXME add associated tys let trait_datum_bound = chalk_rust_ir::TraitDatumBound { trait_ref, where_clauses, flags, associated_ty_ids }; @@ -185,21 +225,26 @@ where let type_ctor = from_chalk(self.db, struct_id); // FIXME might be nicer if we can create a fake GenericParams for the TypeCtor // FIXME extract this to a method on Ty - let (num_params, upstream) = match type_ctor { + let (num_params, where_clauses, upstream) = match type_ctor { TypeCtor::Bool | TypeCtor::Char | TypeCtor::Int(_) | TypeCtor::Float(_) | TypeCtor::Never - | TypeCtor::Str => (0, true), - TypeCtor::Slice | TypeCtor::Array | TypeCtor::RawPtr(_) | TypeCtor::Ref(_) => (1, true), - TypeCtor::FnPtr { num_args } => (num_args as usize + 1, true), - TypeCtor::Tuple { cardinality } => (cardinality as usize, true), + | TypeCtor::Str => (0, vec![], true), + TypeCtor::Slice | TypeCtor::Array | TypeCtor::RawPtr(_) | TypeCtor::Ref(_) => { + (1, vec![], true) + } + TypeCtor::FnPtr { num_args } => (num_args as usize + 1, vec![], true), + TypeCtor::Tuple { cardinality } => (cardinality as usize, vec![], true), TypeCtor::FnDef(_) => unimplemented!(), TypeCtor::Adt(adt) => { let generic_params = adt.generic_params(self.db); + let bound_vars = Substs::bound_vars(&generic_params); + let (where_clauses, _) = convert_where_clauses(self.db, adt.into(), &bound_vars); ( generic_params.count_params_including_parent(), + where_clauses, adt.krate(self.db) != Some(self.krate), ) } @@ -209,7 +254,6 @@ where // FIXME set fundamental flag correctly fundamental: false, }; - let where_clauses = Vec::new(); // FIXME add where clauses let self_ty = chalk_ir::ApplicationTy { name: TypeName::TypeKindId(type_ctor.to_chalk(self.db).into()), parameters: (0..num_params).map(|i| chalk_ir::Ty::BoundVar(i).cast()).collect(), @@ -237,10 +281,12 @@ where } else { chalk_rust_ir::ImplType::External }; + let (where_clauses, where_clause_error) = + convert_where_clauses(self.db, impl_block.into(), &bound_vars); let impl_datum_bound = chalk_rust_ir::ImplDatumBound { // FIXME handle negative impls (impl !Sync for Foo) trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref.to_chalk(self.db)), - where_clauses: Vec::new(), // FIXME add where clauses + where_clauses, associated_ty_values: Vec::new(), // FIXME add associated type values impl_type, }; @@ -253,10 +299,6 @@ where self.db .impls_for_trait(self.krate, trait_) .iter() - // FIXME temporary hack -- as long as we're not lowering where clauses - // correctly, ignore impls with them completely so as to not treat - // impl Trait for T where T: ... as a blanket impl on all types - .filter(|impl_block| impl_block.generic_params(self.db).where_predicates.is_empty()) .map(|impl_block| impl_block.to_chalk(self.db)) .collect() } -- cgit v1.2.3