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,
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,

View file

@ -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<Container> for GenericDef {
fn from(c: Container) -> Self {
match c {

View file

@ -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

View file

@ -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<Ty>,
) -> Option<Self> {
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<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(
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<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
@ -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::<Vec<_>>();
predicates.into()
}
fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig {
let signature = def.signature(db);
let resolver = def.resolver(db);

View file

@ -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<T> Trait for T where T: Clone { fn foo(self) -> u128 { 0 } }
impl<T> 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<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 {

View file

@ -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<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
chalk_ir::Binders {
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>
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<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))
.collect()
}