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/db.rs | 5 ++- crates/ra_hir/src/generics.rs | 19 ++++++++-- crates/ra_hir/src/ty.rs | 31 +++++++++++++++- 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 +++++++++++++++++++++++++++++------- 6 files changed, 188 insertions(+), 29 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 689dd6225..8f98ca3a5 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -11,7 +11,7 @@ use crate::{ DefWithBody, Trait, ids, nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap}, - ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig, TypeCtor}, + ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig, TypeCtor, GenericPredicate}, adt::{StructData, EnumData}, impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock}, generics::{GenericParams, GenericDef}, @@ -138,6 +138,9 @@ pub trait HirDatabase: DefDatabase { #[salsa::invoke(crate::ty::callable_item_sig)] fn callable_item_signature(&self, def: CallableDef) -> FnSig; + #[salsa::invoke(crate::ty::generic_predicates)] + fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>; + #[salsa::invoke(crate::expr::body_with_source_map_query)] fn body_with_source_map( &self, diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 2e52c5871..826117ba5 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use ra_syntax::ast::{self, NameOwner, TypeParamsOwner, TypeBoundsOwner}; use crate::{ - db::DefDatabase, + db::{ HirDatabase, DefDatabase}, Name, AsName, Function, Struct, Enum, Trait, TypeAlias, ImplBlock, Container, path::Path, type_ref::TypeRef, AdtDef }; @@ -32,8 +32,8 @@ pub struct GenericParams { /// where clauses like `where T: Foo + Bar` are turned into multiple of these. #[derive(Clone, PartialEq, Eq, Debug)] pub struct WherePredicate { - type_ref: TypeRef, - trait_ref: Path, + pub(crate) type_ref: TypeRef, + pub(crate) trait_ref: Path, } // FIXME: consts can have type parameters from their parents (i.e. associated consts of traits) @@ -148,6 +148,19 @@ impl GenericParams { } } +impl GenericDef { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> crate::Resolver { + match self { + GenericDef::Function(inner) => inner.resolver(db), + GenericDef::Struct(inner) => inner.resolver(db), + GenericDef::Enum(inner) => inner.resolver(db), + GenericDef::Trait(inner) => inner.resolver(db), + GenericDef::TypeAlias(inner) => inner.resolver(db), + GenericDef::ImplBlock(inner) => inner.resolver(db), + } + } +} + impl From for GenericDef { fn from(c: Container) -> Self { match c { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 12429a668..cfe07156b 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -19,7 +19,7 @@ use std::{fmt, mem}; use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams}; use display::{HirDisplay, HirFormatter}; -pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig}; +pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates}; pub(crate) use infer::{infer, InferenceResult, InferTy}; pub use lower::CallableDef; @@ -234,6 +234,35 @@ impl TraitRef { } } +/// Like `generics::WherePredicate`, but with resolved types: A condition on the +/// parameters of a generic item. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum GenericPredicate { + /// The given trait needs to be implemented for its type parameters. + Implemented(TraitRef), + /// We couldn't resolve the trait reference. (If some type parameters can't + /// be resolved, they will just be Unknown). + Error, +} + +impl GenericPredicate { + pub fn is_error(&self) -> bool { + match self { + GenericPredicate::Error => true, + _ => false, + } + } + + pub fn subst(self, substs: &Substs) -> GenericPredicate { + match self { + GenericPredicate::Implemented(trait_ref) => { + GenericPredicate::Implemented(trait_ref.subst(substs)) + } + GenericPredicate::Error => self, + } + } +} + /// Basically a claim (currently not validated / checked) that the contained /// type / trait ref contains no inference variables; any inference variables it /// contained have been replaced by bound variables, and `num_vars` tells us how 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