From b1b12072eddaf989fb08ed7a2e39ec2dbbb83dde Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Jun 2019 19:14:52 +0200 Subject: Start handling environment in trait resolution I.e. if we are inside a function with some where clauses, we assume these where clauses hold. --- crates/ra_hir/src/ty/infer.rs | 11 +++--- crates/ra_hir/src/ty/lower.rs | 12 +++++++ crates/ra_hir/src/ty/method_resolution.rs | 7 ++-- crates/ra_hir/src/ty/tests.rs | 60 +++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/traits.rs | 8 +++-- crates/ra_hir/src/ty/traits/chalk.rs | 19 ++++++++-- 6 files changed, 103 insertions(+), 14 deletions(-) diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 84edd3d46..f8839ebd2 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -27,10 +27,10 @@ use ra_prof::profile; use test_utils::tested_by; use super::{ - autoderef, method_resolution, op, primitive, + autoderef, lower, method_resolution, op, primitive, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, - ApplicationTy, CallableDef, InEnvironment, ProjectionTy, Substs, TraitRef, Ty, TypableDef, - TypeCtor, + ApplicationTy, CallableDef, Environment, InEnvironment, ProjectionTy, Substs, TraitRef, Ty, + TypableDef, TypeCtor, }; use crate::{ adt::VariantDef, @@ -166,6 +166,7 @@ struct InferenceContext<'a, D: HirDatabase> { body: Arc, resolver: Resolver, var_unification_table: InPlaceUnificationTable, + trait_env: Arc, obligations: Vec, method_resolutions: FxHashMap, field_resolutions: FxHashMap, @@ -189,6 +190,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { var_unification_table: InPlaceUnificationTable::new(), obligations: Vec::default(), return_ty: Ty::Unknown, // set in collect_fn_signature + trait_env: lower::trait_env(db, &resolver), db, body, resolver, @@ -331,8 +333,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for obligation in obligations { match &obligation { Obligation::Trait(tr) => { - let env = Arc::new(super::Environment); // FIXME add environment - let in_env = InEnvironment::new(env, tr.clone()); + let in_env = InEnvironment::new(self.trait_env.clone(), tr.clone()); let canonicalized = self.canonicalizer().canonicalize_trait_ref(in_env); let solution = self .db diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index b48ada760..ca47d6e96 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -317,6 +317,18 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { Ty::from_hir(db, &resolver, type_ref) } +pub(crate) fn trait_env(db: &impl HirDatabase, resolver: &Resolver) -> Arc { + let predicates = resolver + .where_predicates_in_scope() + .map(|pred| { + TraitRef::for_where_predicate(db, &resolver, pred) + .map_or(GenericPredicate::Error, GenericPredicate::Implemented) + }) + .collect::>(); + + Arc::new(super::Environment { predicates }) +} + /// Resolve the where clause(s) of an item with generics. pub(crate) fn generic_predicates_query( db: &impl HirDatabase, diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 770e1964e..40f5eabf0 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use arrayvec::ArrayVec; use rustc_hash::FxHashMap; -use super::{autoderef, Canonical, Environment, InEnvironment, TraitRef}; +use super::{autoderef, lower, Canonical, Environment, InEnvironment, TraitRef}; use crate::{ generics::HasGenericParams, impl_block::{ImplBlock, ImplId, ImplItem}, @@ -198,6 +198,8 @@ fn iterate_trait_method_candidates( mut callback: impl FnMut(&Ty, Function) -> Option, ) -> Option { let krate = resolver.krate()?; + // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) + let env = lower::trait_env(db, resolver); 'traits: for t in resolver.traits_in_scope(db) { let data = t.trait_data(db); // we'll be lazy about checking whether the type implements the @@ -209,8 +211,7 @@ fn iterate_trait_method_candidates( let data = m.data(db); if name.map_or(true, |name| data.name() == name) && data.has_self_param() { if !known_implemented { - let env = Arc::new(super::Environment); // FIXME add environment - let trait_ref = canonical_trait_ref(db, env, t, ty.clone()); + let trait_ref = canonical_trait_ref(db, env.clone(), t, ty.clone()); if db.implements(krate, trait_ref).is_none() { continue 'traits; } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index fe5e89f2d..594e82af2 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2950,6 +2950,66 @@ fn test(o: O) { assert_eq!(t, "&str"); } +#[test] +fn generic_param_env_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(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn generic_param_env_1_not_met() { + 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(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + +#[test] +fn generic_param_env_2() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for S {} +fn test(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "u128"); +} + +#[test] +fn generic_param_env_2_not_met() { + let t = type_at( + r#" +//- /main.rs +trait Trait { fn foo(self) -> u128; } +struct S; +impl Trait for S {} +fn test(t: T) { t.foo()<|>; } +"#, + ); + assert_eq!(t, "{unknown}"); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 718970553..e0c93550a 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -72,11 +72,13 @@ fn solve( /// fn foo(t: T) {} /// ``` /// we assume that `T: Default`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Environment; +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Environment { + pub predicates: Vec, +} /// Something (usually a goal), along with an environment. -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct InEnvironment { pub environment: Arc, pub value: T, diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index bee35fa62..f36ff2fc6 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -239,15 +239,28 @@ where impl ToChalk for Arc { type Chalk = Arc; - fn to_chalk(self, _db: &impl HirDatabase) -> Arc { - chalk_ir::Environment::new() + fn to_chalk(self, db: &impl HirDatabase) -> Arc { + let mut clauses = Vec::new(); + for pred in &self.predicates { + if pred.is_error() { + // for env, we just ignore errors + continue; + } + if let GenericPredicate::Implemented(trait_ref) = pred { + if blacklisted_trait(db, trait_ref.trait_) { + continue; + } + } + clauses.push(pred.clone().to_chalk(db).cast()); + } + chalk_ir::Environment::new().add_clauses(clauses) } fn from_chalk( _db: &impl HirDatabase, _env: Arc, ) -> Arc { - Arc::new(super::Environment) + unimplemented!() } } -- cgit v1.2.3