From 638100dc8bea69cc4093d15f1641ed39a8d27a43 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Jun 2019 17:40:00 +0200 Subject: Refactor a bit & introduce Environment struct --- crates/ra_hir/src/db.rs | 2 +- crates/ra_hir/src/ty.rs | 2 +- crates/ra_hir/src/ty/infer.rs | 7 ++-- crates/ra_hir/src/ty/infer/unify.rs | 20 ++++++++--- crates/ra_hir/src/ty/method_resolution.rs | 10 +++--- crates/ra_hir/src/ty/traits.rs | 31 ++++++++++++----- crates/ra_hir/src/ty/traits/chalk.rs | 56 ++++++++++++++++++++++++++++++- 7 files changed, 107 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 0e6e3fdb7..9d46f9025 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -217,7 +217,7 @@ pub trait HirDatabase: DefDatabase + AstDatabase { fn implements( &self, krate: Crate, - goal: crate::ty::Canonical, + goal: crate::ty::Canonical>, ) -> Option; #[salsa::invoke(crate::ty::traits::normalize_query)] diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d8c7945e1..b45a188d1 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -26,7 +26,7 @@ pub(crate) use lower::{ callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def, type_for_field, TypableDef, }; -pub(crate) use traits::ProjectionPredicate; +pub(crate) use traits::{Environment, InEnvironment, ProjectionPredicate}; /// A type constructor or type name: this might be something like the primitive /// type `bool`, a struct like `Vec`, or things like function pointers or diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 52a49070a..84edd3d46 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -29,7 +29,8 @@ use test_utils::tested_by; use super::{ autoderef, method_resolution, op, primitive, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, - ApplicationTy, CallableDef, ProjectionTy, Substs, TraitRef, Ty, TypableDef, TypeCtor, + ApplicationTy, CallableDef, InEnvironment, ProjectionTy, Substs, TraitRef, Ty, TypableDef, + TypeCtor, }; use crate::{ adt::VariantDef, @@ -330,7 +331,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for obligation in obligations { match &obligation { Obligation::Trait(tr) => { - let canonicalized = self.canonicalizer().canonicalize_trait_ref(tr.clone()); + let env = Arc::new(super::Environment); // FIXME add environment + let in_env = InEnvironment::new(env, tr.clone()); + let canonicalized = self.canonicalizer().canonicalize_trait_ref(in_env); let solution = self .db .implements(self.resolver.krate().unwrap(), canonicalized.value.clone()); diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs index a24e5eb5c..ad2eefcaf 100644 --- a/crates/ra_hir/src/ty/infer/unify.rs +++ b/crates/ra_hir/src/ty/infer/unify.rs @@ -2,7 +2,9 @@ use super::InferenceContext; use crate::db::HirDatabase; -use crate::ty::{Canonical, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty}; +use crate::ty::{ + Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, +}; impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D> @@ -105,14 +107,24 @@ where ProjectionPredicate { ty, projection_ty } } + // FIXME: add some point, we need to introduce a `Fold` trait that abstracts + // over all the things that can be canonicalized (like Chalk and rustc have) + pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized { let result = self.do_canonicalize_ty(ty); self.into_canonicalized(result) } - pub fn canonicalize_trait_ref(mut self, trait_ref: TraitRef) -> Canonicalized { - let result = self.do_canonicalize_trait_ref(trait_ref); - self.into_canonicalized(result) + pub fn canonicalize_trait_ref( + mut self, + trait_ref_in_env: InEnvironment, + ) -> Canonicalized> { + let result = self.do_canonicalize_trait_ref(trait_ref_in_env.value); + // FIXME canonicalize env + self.into_canonicalized(InEnvironment { + value: result, + environment: trait_ref_in_env.environment, + }) } pub fn canonicalize_projection( diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 76ace66ea..770e1964e 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, TraitRef}; +use super::{autoderef, Canonical, Environment, InEnvironment, TraitRef}; use crate::{ generics::HasGenericParams, impl_block::{ImplBlock, ImplId, ImplItem}, @@ -209,7 +209,8 @@ 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 trait_ref = canonical_trait_ref(db, t, ty.clone()); + let env = Arc::new(super::Environment); // FIXME add environment + let trait_ref = canonical_trait_ref(db, env, t, ty.clone()); if db.implements(krate, trait_ref).is_none() { continue 'traits; } @@ -279,9 +280,10 @@ impl Ty { /// for all other parameters, to query Chalk with it. fn canonical_trait_ref( db: &impl HirDatabase, + env: Arc, trait_: Trait, self_ty: Canonical, -) -> Canonical { +) -> Canonical> { let mut substs = Vec::new(); let generics = trait_.generic_params(db); let num_vars = self_ty.num_vars; @@ -296,6 +298,6 @@ fn canonical_trait_ref( ); Canonical { num_vars: substs.len() - 1 + self_ty.num_vars, - value: TraitRef { trait_, substs: substs.into() }, + value: InEnvironment::new(env, TraitRef { trait_, substs: substs.into() }), } } diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 3e28852b6..718970553 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -67,6 +67,27 @@ fn solve( solution } +/// A set of clauses that we assume to be true. E.g. if we are inside this function: +/// ```rust +/// fn foo(t: T) {} +/// ``` +/// we assume that `T: Default`. +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Environment; + +/// Something (usually a goal), along with an environment. +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct InEnvironment { + pub environment: Arc, + pub value: T, +} + +impl InEnvironment { + pub fn new(environment: Arc, value: T) -> InEnvironment { + InEnvironment { environment, value } + } +} + /// Something that needs to be proven (by Chalk) during type checking, e.g. that /// a certain type implements a certain trait. Proving the Obligation might /// result in additional information about inference variables. @@ -97,16 +118,10 @@ pub struct ProjectionPredicate { pub(crate) fn implements_query( db: &impl HirDatabase, krate: Crate, - trait_ref: Canonical, + trait_ref: Canonical>, ) -> Option { let _p = profile("implements_query"); - let goal: chalk_ir::Goal = trait_ref.value.to_chalk(db).cast(); - debug!("goal: {:?}", goal); - let env = chalk_ir::Environment::new(); - let in_env = chalk_ir::InEnvironment::new(&env, goal); - let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); - let canonical = - chalk_ir::Canonical { value: in_env, binders: vec![parameter; trait_ref.num_vars] }; + let canonical = trait_ref.to_chalk(db).cast(); // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 4c3744b44..bee35fa62 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -12,7 +12,7 @@ use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum}; use ra_db::salsa::{InternId, InternKey}; use test_utils::tested_by; -use super::ChalkContext; +use super::{Canonical, ChalkContext}; use crate::{ db::HirDatabase, generics::GenericDef, @@ -218,6 +218,60 @@ impl ToChalk for ProjectionTy { } } +impl ToChalk for Canonical +where + T: ToChalk, +{ + type Chalk = chalk_ir::Canonical; + + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Canonical { + let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); + let value = self.value.to_chalk(db); + let canonical = chalk_ir::Canonical { value, binders: vec![parameter; self.num_vars] }; + canonical + } + + fn from_chalk(db: &impl HirDatabase, canonical: chalk_ir::Canonical) -> Canonical { + Canonical { num_vars: canonical.binders.len(), value: from_chalk(db, canonical.value) } + } +} + +impl ToChalk for Arc { + type Chalk = Arc; + + fn to_chalk(self, _db: &impl HirDatabase) -> Arc { + chalk_ir::Environment::new() + } + + fn from_chalk( + _db: &impl HirDatabase, + _env: Arc, + ) -> Arc { + Arc::new(super::Environment) + } +} + +impl ToChalk for super::InEnvironment { + type Chalk = chalk_ir::InEnvironment; + + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::InEnvironment { + chalk_ir::InEnvironment { + environment: self.environment.to_chalk(db), + goal: self.value.to_chalk(db), + } + } + + fn from_chalk( + db: &impl HirDatabase, + in_env: chalk_ir::InEnvironment, + ) -> super::InEnvironment { + super::InEnvironment { + environment: from_chalk(db, in_env.environment), + value: from_chalk(db, in_env.goal), + } + } +} + fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders { chalk_ir::Binders { value, -- cgit v1.2.3 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 From 15862fc04183c7f9b3f3af666336a594a6a52cd9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 7 Jul 2019 18:14:56 +0200 Subject: Use environment for associated type normalization as well --- crates/ra_hir/src/db.rs | 2 +- crates/ra_hir/src/ty/autoderef.rs | 6 +++++- crates/ra_hir/src/ty/infer.rs | 3 ++- crates/ra_hir/src/ty/infer/unify.rs | 12 ++++++++---- crates/ra_hir/src/ty/tests.rs | 19 +++++++++++++++++++ crates/ra_hir/src/ty/traits.rs | 15 ++------------- crates/ra_hir/src/ty/traits/chalk.rs | 15 +++++++++++++++ 7 files changed, 52 insertions(+), 20 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 9d46f9025..e0a37e13d 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -224,7 +224,7 @@ pub trait HirDatabase: DefDatabase + AstDatabase { fn normalize( &self, krate: Crate, - goal: crate::ty::Canonical, + goal: crate::ty::Canonical>, ) -> Option; } diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index 90c1ae630..214aa7d03 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -52,6 +52,8 @@ fn deref_by_trait( // FIXME make the Canonical handling nicer + let env = super::lower::trait_env(db, resolver); + let projection = super::traits::ProjectionPredicate { ty: Ty::Bound(0), projection_ty: super::ProjectionTy { @@ -60,7 +62,9 @@ fn deref_by_trait( }, }; - let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection }; + let in_env = super::traits::InEnvironment { value: projection, environment: env }; + + let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env }; let solution = db.normalize(krate, canonical)?; diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index f8839ebd2..f6cf61ad2 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -356,7 +356,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; } Obligation::Projection(pr) => { - let canonicalized = self.canonicalizer().canonicalize_projection(pr.clone()); + let in_env = InEnvironment::new(self.trait_env.clone(), pr.clone()); + let canonicalized = self.canonicalizer().canonicalize_projection(in_env); let solution = self .db .normalize(self.resolver.krate().unwrap(), canonicalized.value.clone()); diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs index ad2eefcaf..2ed326cd5 100644 --- a/crates/ra_hir/src/ty/infer/unify.rs +++ b/crates/ra_hir/src/ty/infer/unify.rs @@ -129,10 +129,14 @@ where pub fn canonicalize_projection( mut self, - projection: ProjectionPredicate, - ) -> Canonicalized { - let result = self.do_canonicalize_projection_predicate(projection); - self.into_canonicalized(result) + projection: InEnvironment, + ) -> Canonicalized> { + let result = self.do_canonicalize_projection_predicate(projection.value); + // FIXME canonicalize env + self.into_canonicalized(InEnvironment { + value: result, + environment: projection.environment, + }) } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 594e82af2..7340bb9bd 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3010,6 +3010,25 @@ fn test(t: T) { t.foo()<|>; } assert_eq!(t, "{unknown}"); } +#[test] +fn generic_param_env_deref() { + let t = type_at( + r#" +//- /main.rs +#[lang = "deref"] +trait Deref { + type Target; +} +trait Trait {} +impl Deref for T where T: Trait { + type Target = i128; +} +fn test(t: T) { (*t)<|>; } +"#, + ); + assert_eq!(t, "i128"); +} + 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 e0c93550a..01f350bc1 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -134,20 +134,9 @@ pub(crate) fn implements_query( pub(crate) fn normalize_query( db: &impl HirDatabase, krate: Crate, - projection: Canonical, + projection: Canonical>, ) -> Option { - let goal: chalk_ir::Goal = chalk_ir::Normalize { - projection: projection.value.projection_ty.to_chalk(db), - ty: projection.value.ty.to_chalk(db), - } - .cast(); - debug!("goal: {:?}", goal); - // FIXME unify with `implements` - let env = chalk_ir::Environment::new(); - let in_env = chalk_ir::InEnvironment::new(&env, goal); - let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); - let canonical = - chalk_ir::Canonical { value: in_env, binders: vec![parameter; projection.num_vars] }; + let canonical = projection.to_chalk(db).cast(); // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index f36ff2fc6..32a45731d 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -218,6 +218,21 @@ impl ToChalk for ProjectionTy { } } +impl ToChalk for super::ProjectionPredicate { + type Chalk = chalk_ir::Normalize; + + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize { + chalk_ir::Normalize { + projection: self.projection_ty.to_chalk(db), + ty: self.ty.to_chalk(db), + } + } + + fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize) -> Self { + unimplemented!() + } +} + impl ToChalk for Canonical where T: ToChalk, -- cgit v1.2.3 From 9afbf2dff43dee3227358f10162d4c77d192ce7a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 8 Jul 2019 21:43:52 +0200 Subject: Unify `normalize` and `implements` to simplify code --- crates/ra_hir/src/db.rs | 13 ++----- crates/ra_hir/src/ty.rs | 2 +- crates/ra_hir/src/ty/autoderef.rs | 6 ++- crates/ra_hir/src/ty/infer.rs | 64 +++++++++---------------------- crates/ra_hir/src/ty/infer/unify.rs | 32 ++++++---------- crates/ra_hir/src/ty/method_resolution.rs | 16 ++++---- crates/ra_hir/src/ty/traits.rs | 21 ++-------- crates/ra_hir/src/ty/traits/chalk.rs | 17 +++++++- crates/ra_ide_api/src/change.rs | 3 +- 9 files changed, 67 insertions(+), 107 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index e0a37e13d..b0c027631 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -213,18 +213,11 @@ pub trait HirDatabase: DefDatabase + AstDatabase { #[salsa::invoke(crate::ty::traits::chalk::impl_datum_query)] fn impl_datum(&self, krate: Crate, impl_id: chalk_ir::ImplId) -> Arc; - #[salsa::invoke(crate::ty::traits::implements_query)] - fn implements( + #[salsa::invoke(crate::ty::traits::solve_query)] + fn solve( &self, krate: Crate, - goal: crate::ty::Canonical>, - ) -> Option; - - #[salsa::invoke(crate::ty::traits::normalize_query)] - fn normalize( - &self, - krate: Crate, - goal: crate::ty::Canonical>, + goal: crate::ty::Canonical>, ) -> Option; } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index b45a188d1..9accffcbc 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -26,7 +26,7 @@ pub(crate) use lower::{ callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def, type_for_field, TypableDef, }; -pub(crate) use traits::{Environment, InEnvironment, ProjectionPredicate}; +pub(crate) use traits::{Environment, InEnvironment, Obligation, ProjectionPredicate}; /// A type constructor or type name: this might be something like the primitive /// type `bool`, a struct like `Vec`, or things like function pointers or diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index 214aa7d03..c26513871 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -62,11 +62,13 @@ fn deref_by_trait( }, }; - let in_env = super::traits::InEnvironment { value: projection, environment: env }; + let obligation = super::Obligation::Projection(projection); + + let in_env = super::traits::InEnvironment { value: obligation, environment: env }; let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env }; - let solution = db.normalize(krate, canonical)?; + let solution = db.solve(krate, canonical)?; match &solution { Solution::Unique(vars) => { diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index f6cf61ad2..1f74108a4 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -331,53 +331,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn resolve_obligations_as_possible(&mut self) { let obligations = mem::replace(&mut self.obligations, Vec::new()); for obligation in obligations { - match &obligation { - Obligation::Trait(tr) => { - let in_env = InEnvironment::new(self.trait_env.clone(), tr.clone()); - let canonicalized = self.canonicalizer().canonicalize_trait_ref(in_env); - let solution = self - .db - .implements(self.resolver.krate().unwrap(), canonicalized.value.clone()); - match solution { - Some(Solution::Unique(substs)) => { - canonicalized.apply_solution(self, substs.0); - } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs.0); - self.obligations.push(obligation); - } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - self.obligations.push(obligation); - } - None => { - // FIXME obligation cannot be fulfilled => diagnostic - } - }; + let in_env = InEnvironment::new(self.trait_env.clone(), obligation.clone()); + let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); + let solution = + self.db.solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); + + match solution { + Some(Solution::Unique(substs)) => { + canonicalized.apply_solution(self, substs.0); } - Obligation::Projection(pr) => { - let in_env = InEnvironment::new(self.trait_env.clone(), pr.clone()); - let canonicalized = self.canonicalizer().canonicalize_projection(in_env); - let solution = self - .db - .normalize(self.resolver.krate().unwrap(), canonicalized.value.clone()); - - match solution { - Some(Solution::Unique(substs)) => { - canonicalized.apply_solution(self, substs.0); - } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs.0); - self.obligations.push(obligation); - } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - self.obligations.push(obligation); - } - None => { - // FIXME obligation cannot be fulfilled => diagnostic - } - }; + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(self, substs.0); + self.obligations.push(obligation); + } + Some(_) => { + // FIXME use this when trying to resolve everything at the end + self.obligations.push(obligation); + } + None => { + // FIXME obligation cannot be fulfilled => diagnostic } }; } diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs index 2ed326cd5..e7e8825d1 100644 --- a/crates/ra_hir/src/ty/infer/unify.rs +++ b/crates/ra_hir/src/ty/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use super::InferenceContext; +use super::{InferenceContext, Obligation}; use crate::db::HirDatabase; use crate::ty::{ Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, @@ -110,32 +110,24 @@ where // FIXME: add some point, we need to introduce a `Fold` trait that abstracts // over all the things that can be canonicalized (like Chalk and rustc have) - pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized { + pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized { let result = self.do_canonicalize_ty(ty); self.into_canonicalized(result) } - pub fn canonicalize_trait_ref( + pub(crate) fn canonicalize_obligation( mut self, - trait_ref_in_env: InEnvironment, - ) -> Canonicalized> { - let result = self.do_canonicalize_trait_ref(trait_ref_in_env.value); - // FIXME canonicalize env - self.into_canonicalized(InEnvironment { - value: result, - environment: trait_ref_in_env.environment, - }) - } - - pub fn canonicalize_projection( - mut self, - projection: InEnvironment, - ) -> Canonicalized> { - let result = self.do_canonicalize_projection_predicate(projection.value); - // FIXME canonicalize env + obligation: InEnvironment, + ) -> Canonicalized> { + let result = match obligation.value { + Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize_trait_ref(tr)), + Obligation::Projection(pr) => { + Obligation::Projection(self.do_canonicalize_projection_predicate(pr)) + } + }; self.into_canonicalized(InEnvironment { value: result, - environment: projection.environment, + environment: obligation.environment, }) } } diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 40f5eabf0..e214bf1af 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -211,8 +211,8 @@ 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 trait_ref = canonical_trait_ref(db, env.clone(), t, ty.clone()); - if db.implements(krate, trait_ref).is_none() { + let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); + if db.solve(krate, goal).is_none() { continue 'traits; } } @@ -279,12 +279,12 @@ impl Ty { /// This creates Substs for a trait with the given Self type and type variables /// for all other parameters, to query Chalk with it. -fn canonical_trait_ref( +fn generic_implements_goal( db: &impl HirDatabase, env: Arc, trait_: Trait, self_ty: Canonical, -) -> Canonical> { +) -> Canonical> { let mut substs = Vec::new(); let generics = trait_.generic_params(db); let num_vars = self_ty.num_vars; @@ -297,8 +297,8 @@ fn canonical_trait_ref( .enumerate() .map(|(i, _p)| Ty::Bound((i + num_vars) as u32)), ); - Canonical { - num_vars: substs.len() - 1 + self_ty.num_vars, - value: InEnvironment::new(env, TraitRef { trait_, substs: substs.into() }), - } + let num_vars = substs.len() - 1 + self_ty.num_vars; + let trait_ref = TraitRef { trait_, substs: substs.into() }; + let obligation = super::Obligation::Trait(trait_ref); + Canonical { num_vars, value: InEnvironment::new(env, obligation) } } diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 01f350bc1..d99843319 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -93,7 +93,7 @@ impl InEnvironment { /// Something that needs to be proven (by Chalk) during type checking, e.g. that /// a certain type implements a certain trait. Proving the Obligation might /// result in additional information about inference variables. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Obligation { /// Prove that a certain type implements a trait (the type is the `Self` type /// parameter to the `TraitRef`). @@ -116,11 +116,11 @@ pub struct ProjectionPredicate { pub ty: Ty, } -/// Check using Chalk whether trait is implemented for given parameters including `Self` type. -pub(crate) fn implements_query( +/// Solve a trait goal using Chalk. +pub(crate) fn solve_query( db: &impl HirDatabase, krate: Crate, - trait_ref: Canonical>, + trait_ref: Canonical>, ) -> Option { let _p = profile("implements_query"); let canonical = trait_ref.to_chalk(db).cast(); @@ -131,19 +131,6 @@ pub(crate) fn implements_query( solution.map(|solution| solution_from_chalk(db, solution)) } -pub(crate) fn normalize_query( - db: &impl HirDatabase, - krate: Crate, - projection: Canonical>, -) -> Option { - let canonical = projection.to_chalk(db).cast(); - // We currently don't deal with universes (I think / hope they're not yet - // relevant for our use cases?) - let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; - let solution = solve(db, krate, &u_canonical); - solution.map(|solution| solution_from_chalk(db, solution)) -} - fn solution_from_chalk(db: &impl HirDatabase, solution: chalk_solve::Solution) -> Solution { let convert_subst = |subst: chalk_ir::Canonical| { let value = subst diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 32a45731d..2df4dd13f 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -12,7 +12,7 @@ use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum}; use ra_db::salsa::{InternId, InternKey}; use test_utils::tested_by; -use super::{Canonical, ChalkContext}; +use super::{Canonical, ChalkContext, Obligation}; use crate::{ db::HirDatabase, generics::GenericDef, @@ -233,6 +233,21 @@ impl ToChalk for super::ProjectionPredicate { } } +impl ToChalk for Obligation { + type Chalk = chalk_ir::DomainGoal; + + fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal { + match self { + Obligation::Trait(tr) => tr.to_chalk(db).cast(), + Obligation::Projection(pr) => pr.to_chalk(db).cast(), + } + } + + fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal) -> Self { + unimplemented!() + } +} + impl ToChalk for Canonical where T: ToChalk, diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 2a1af0a0a..efc9a92de 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs @@ -295,8 +295,7 @@ impl RootDatabase { hir::db::TraitDatumQuery hir::db::StructDatumQuery hir::db::ImplDatumQuery - hir::db::ImplementsQuery - hir::db::NormalizeQuery + hir::db::SolveQuery ]; acc.sort_by_key(|it| std::cmp::Reverse(it.1)); acc -- cgit v1.2.3