Handle where clauses in trait solving

This commit is contained in:
Florian Diebold 2019-05-05 14:21:00 +02:00
parent 940c538ecf
commit 50bbf9eb09
6 changed files with 188 additions and 29 deletions

View file

@ -11,7 +11,7 @@ use crate::{
DefWithBody, Trait, DefWithBody, Trait,
ids, ids,
nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap}, 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}, adt::{StructData, EnumData},
impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock}, impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock},
generics::{GenericParams, GenericDef}, generics::{GenericParams, GenericDef},
@ -138,6 +138,9 @@ pub trait HirDatabase: DefDatabase {
#[salsa::invoke(crate::ty::callable_item_sig)] #[salsa::invoke(crate::ty::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDef) -> FnSig; 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)] #[salsa::invoke(crate::expr::body_with_source_map_query)]
fn body_with_source_map( fn body_with_source_map(
&self, &self,

View file

@ -8,7 +8,7 @@ use std::sync::Arc;
use ra_syntax::ast::{self, NameOwner, TypeParamsOwner, TypeBoundsOwner}; use ra_syntax::ast::{self, NameOwner, TypeParamsOwner, TypeBoundsOwner};
use crate::{ use crate::{
db::DefDatabase, db::{ HirDatabase, DefDatabase},
Name, AsName, Function, Struct, Enum, Trait, TypeAlias, ImplBlock, Container, path::Path, type_ref::TypeRef, AdtDef 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. /// where clauses like `where T: Foo + Bar` are turned into multiple of these.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct WherePredicate { pub struct WherePredicate {
type_ref: TypeRef, pub(crate) type_ref: TypeRef,
trait_ref: Path, pub(crate) trait_ref: Path,
} }
// FIXME: consts can have type parameters from their parents (i.e. associated consts of traits) // 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<Container> for GenericDef { impl From<Container> for GenericDef {
fn from(c: Container) -> Self { fn from(c: Container) -> Self {
match c { match c {

View file

@ -19,7 +19,7 @@ use std::{fmt, mem};
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams}; use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams};
use display::{HirDisplay, HirFormatter}; 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(crate) use infer::{infer, InferenceResult, InferTy};
pub use lower::CallableDef; 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 /// Basically a claim (currently not validated / checked) that the contained
/// type / trait ref contains no inference variables; any inference variables it /// type / trait ref contains no inference variables; any inference variables it
/// contained have been replaced by bound variables, and `num_vars` tells us how /// contained have been replaced by bound variables, and `num_vars` tells us how

View file

@ -5,6 +5,7 @@
//! - Building the type for an item: This happens through the `type_for_def` query. //! - Building the type for an item: This happens through the `type_for_def` query.
//! //!
//! This usually involves resolving names, collecting generic arguments etc. //! This usually involves resolving names, collecting generic arguments etc.
use std::sync::Arc;
use std::iter; use std::iter;
use crate::{ use crate::{
@ -18,9 +19,9 @@ use crate::{
resolve::{Resolver, Resolution}, resolve::{Resolver, Resolution},
path::{PathSegment, GenericArg}, path::{PathSegment, GenericArg},
generics::{GenericParams, HasGenericParams}, 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 { impl Ty {
pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self { 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 { impl TraitRef {
pub(crate) fn from_hir( pub(crate) fn from_path(
db: &impl HirDatabase, db: &impl HirDatabase,
resolver: &Resolver, resolver: &Resolver,
type_ref: &TypeRef, path: &Path,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
) -> Option<Self> { ) -> Option<Self> {
let path = match type_ref {
TypeRef::Path(path) => path,
_ => return None,
};
let resolved = match resolver.resolve_path(db, &path).take_types()? { let resolved = match resolver.resolve_path(db, &path).take_types()? {
Resolution::Def(ModuleDef::Trait(tr)) => tr, Resolution::Def(ModuleDef::Trait(tr)) => tr,
_ => return None, _ => return None,
@ -232,6 +229,19 @@ impl TraitRef {
Some(TraitRef { trait_: resolved, substs }) Some(TraitRef { trait_: resolved, substs })
} }
pub(crate) fn from_hir(
db: &impl HirDatabase,
resolver: &Resolver,
type_ref: &TypeRef,
explicit_self_ty: Option<Ty>,
) -> Option<Self> {
let path = match type_ref {
TypeRef::Path(path) => path,
_ => return None,
};
TraitRef::from_path(db, resolver, path, explicit_self_ty)
}
fn substs_from_path( fn substs_from_path(
db: &impl HirDatabase, db: &impl HirDatabase,
resolver: &Resolver, resolver: &Resolver,
@ -246,6 +256,15 @@ impl TraitRef {
let substs = Substs::identity(&trait_.generic_params(db)); let substs = Substs::identity(&trait_.generic_params(db));
TraitRef { trait_, substs } TraitRef { trait_, substs }
} }
pub(crate) fn for_where_predicate(
db: &impl HirDatabase,
resolver: &Resolver,
pred: &WherePredicate,
) -> Option<TraitRef> {
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 /// 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) 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::<Vec<_>>();
predicates.into()
}
fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig { fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig {
let signature = def.signature(db); let signature = def.signature(db);
let resolver = def.resolver(db); let resolver = def.resolver(db);

View file

@ -2510,12 +2510,47 @@ fn method_resolution_where_clause_not_met() {
trait Clone {} trait Clone {}
trait Trait { fn foo(self) -> u128; } trait Trait { fn foo(self) -> u128; }
struct S; struct S;
impl S { fn foo(self) -> i8 { 0 } } impl<T> Trait for T where T: Clone {}
impl<T> Trait for T where T: Clone { fn foo(self) -> u128 { 0 } }
fn test() { (&S).foo()<|>; } 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<T> 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<T> { fn into(self) -> T; }
trait From<T> { fn from(other: T) -> Self; }
struct S1;
struct S2;
impl From<S2> for S1 {};
impl<T, U> Into<U> for T where U: From<T> {}
fn test() { S2.into()<|>; }
"#,
);
assert_eq!(t, "S1");
} }
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {

View file

@ -11,7 +11,7 @@ use ra_db::salsa::{InternId, InternKey};
use crate::{ use crate::{
Trait, HasGenericParams, ImplBlock, Trait, HasGenericParams, ImplBlock,
db::HirDatabase, db::HirDatabase,
ty::{TraitRef, Ty, ApplicationTy, TypeCtor, Substs}, ty::{TraitRef, Ty, ApplicationTy, TypeCtor, Substs, GenericPredicate}, generics::GenericDef,
}; };
use super::ChalkContext; 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<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> { fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
chalk_ir::Binders { chalk_ir::Binders {
value, value,
@ -153,6 +174,25 @@ fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
} }
} }
fn convert_where_clauses(
db: &impl HirDatabase,
def: GenericDef,
substs: &Substs,
) -> (Vec<chalk_ir::QuantifiedWhereClause>, 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> impl<'a, DB> chalk_solve::RustIrDatabase for ChalkContext<'a, DB>
where where
DB: HirDatabase, DB: HirDatabase,
@ -173,7 +213,7 @@ where
upstream: trait_.module(self.db).krate(self.db) != Some(self.krate), upstream: trait_.module(self.db).krate(self.db) != Some(self.krate),
fundamental: false, 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 associated_ty_ids = Vec::new(); // FIXME add associated tys
let trait_datum_bound = let trait_datum_bound =
chalk_rust_ir::TraitDatumBound { trait_ref, where_clauses, flags, associated_ty_ids }; 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); let type_ctor = from_chalk(self.db, struct_id);
// FIXME might be nicer if we can create a fake GenericParams for the TypeCtor // FIXME might be nicer if we can create a fake GenericParams for the TypeCtor
// FIXME extract this to a method on Ty // 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::Bool
| TypeCtor::Char | TypeCtor::Char
| TypeCtor::Int(_) | TypeCtor::Int(_)
| TypeCtor::Float(_) | TypeCtor::Float(_)
| TypeCtor::Never | TypeCtor::Never
| TypeCtor::Str => (0, true), | TypeCtor::Str => (0, vec![], true),
TypeCtor::Slice | TypeCtor::Array | TypeCtor::RawPtr(_) | TypeCtor::Ref(_) => (1, true), TypeCtor::Slice | TypeCtor::Array | TypeCtor::RawPtr(_) | TypeCtor::Ref(_) => {
TypeCtor::FnPtr { num_args } => (num_args as usize + 1, true), (1, vec![], true)
TypeCtor::Tuple { cardinality } => (cardinality as usize, true), }
TypeCtor::FnPtr { num_args } => (num_args as usize + 1, vec![], true),
TypeCtor::Tuple { cardinality } => (cardinality as usize, vec![], true),
TypeCtor::FnDef(_) => unimplemented!(), TypeCtor::FnDef(_) => unimplemented!(),
TypeCtor::Adt(adt) => { TypeCtor::Adt(adt) => {
let generic_params = adt.generic_params(self.db); 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(), generic_params.count_params_including_parent(),
where_clauses,
adt.krate(self.db) != Some(self.krate), adt.krate(self.db) != Some(self.krate),
) )
} }
@ -209,7 +254,6 @@ where
// FIXME set fundamental flag correctly // FIXME set fundamental flag correctly
fundamental: false, fundamental: false,
}; };
let where_clauses = Vec::new(); // FIXME add where clauses
let self_ty = chalk_ir::ApplicationTy { let self_ty = chalk_ir::ApplicationTy {
name: TypeName::TypeKindId(type_ctor.to_chalk(self.db).into()), name: TypeName::TypeKindId(type_ctor.to_chalk(self.db).into()),
parameters: (0..num_params).map(|i| chalk_ir::Ty::BoundVar(i).cast()).collect(), parameters: (0..num_params).map(|i| chalk_ir::Ty::BoundVar(i).cast()).collect(),
@ -237,10 +281,12 @@ where
} else { } else {
chalk_rust_ir::ImplType::External 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 { let impl_datum_bound = chalk_rust_ir::ImplDatumBound {
// FIXME handle negative impls (impl !Sync for Foo) // FIXME handle negative impls (impl !Sync for Foo)
trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref.to_chalk(self.db)), 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 associated_ty_values: Vec::new(), // FIXME add associated type values
impl_type, impl_type,
}; };
@ -253,10 +299,6 @@ where
self.db self.db
.impls_for_trait(self.krate, trait_) .impls_for_trait(self.krate, trait_)
.iter() .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<T> 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)) .map(|impl_block| impl_block.to_chalk(self.db))
.collect() .collect()
} }