Start handling environment in trait resolution

I.e. if we are inside a function with some where clauses, we assume these where
clauses hold.
This commit is contained in:
Florian Diebold 2019-06-29 19:14:52 +02:00
parent 638100dc8b
commit b1b12072ed
6 changed files with 103 additions and 14 deletions

View file

@ -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<Body>,
resolver: Resolver,
var_unification_table: InPlaceUnificationTable<TypeVarId>,
trait_env: Arc<Environment>,
obligations: Vec<Obligation>,
method_resolutions: FxHashMap<ExprId, Function>,
field_resolutions: FxHashMap<ExprId, StructField>,
@ -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

View file

@ -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<super::Environment> {
let predicates = resolver
.where_predicates_in_scope()
.map(|pred| {
TraitRef::for_where_predicate(db, &resolver, pred)
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
})
.collect::<Vec<_>>();
Arc::new(super::Environment { predicates })
}
/// Resolve the where clause(s) of an item with generics.
pub(crate) fn generic_predicates_query(
db: &impl HirDatabase,

View file

@ -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<T>(
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
) -> Option<T> {
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<T>(
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;
}

View file

@ -2950,6 +2950,66 @@ fn test(o: O<S>) {
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<T> Trait for T where T: Clone {}
fn test<T: Clone>(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<T> Trait for T where T: Clone {}
fn test<T>(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: Trait>(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) { 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::<ast::Expr>(file.syntax(), pos.offset).unwrap();

View file

@ -72,11 +72,13 @@ fn solve(
/// fn foo<T: Default>(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<GenericPredicate>,
}
/// 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<T> {
pub environment: Arc<Environment>,
pub value: T,

View file

@ -239,15 +239,28 @@ where
impl ToChalk for Arc<super::Environment> {
type Chalk = Arc<chalk_ir::Environment>;
fn to_chalk(self, _db: &impl HirDatabase) -> Arc<chalk_ir::Environment> {
chalk_ir::Environment::new()
fn to_chalk(self, db: &impl HirDatabase) -> Arc<chalk_ir::Environment> {
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<chalk_ir::Environment>,
) -> Arc<super::Environment> {
Arc::new(super::Environment)
unimplemented!()
}
}