mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
Handle where clauses in trait solving
This commit is contained in:
parent
940c538ecf
commit
50bbf9eb09
6 changed files with 188 additions and 29 deletions
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue