mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
Add trait obligations for where clauses when calling functions/methods
E.g. if we call `foo<T: Into<u32>>(x)`, that adds an obligation that `x: Into<u32>`, etc.
This commit is contained in:
parent
219e0e8c8d
commit
065d398701
7 changed files with 156 additions and 34 deletions
|
@ -163,10 +163,10 @@ pub trait HirDatabase: DefDatabase + AstDatabase {
|
||||||
#[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)]
|
#[salsa::invoke(crate::ty::generic_predicates_query)]
|
||||||
fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>;
|
fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::ty::generic_defaults)]
|
#[salsa::invoke(crate::ty::generic_defaults_query)]
|
||||||
fn generic_defaults(&self, def: GenericDef) -> Substs;
|
fn generic_defaults(&self, def: GenericDef) -> Substs;
|
||||||
|
|
||||||
#[salsa::invoke(crate::expr::body_with_source_map_query)]
|
#[salsa::invoke(crate::expr::body_with_source_map_query)]
|
||||||
|
|
|
@ -221,6 +221,18 @@ impl Resolver {
|
||||||
pub(crate) fn krate(&self) -> Option<Crate> {
|
pub(crate) fn krate(&self) -> Option<Crate> {
|
||||||
self.module().map(|t| t.0.krate())
|
self.module().map(|t| t.0.krate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn where_predicates_in_scope<'a>(
|
||||||
|
&'a self,
|
||||||
|
) -> impl Iterator<Item = &'a crate::generics::WherePredicate> + 'a {
|
||||||
|
self.scopes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|scope| match scope {
|
||||||
|
Scope::GenericParams(params) => Some(params),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.flat_map(|params| params.where_predicates.iter())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver {
|
impl Resolver {
|
||||||
|
|
|
@ -23,8 +23,8 @@ pub(crate) use autoderef::autoderef;
|
||||||
pub(crate) use infer::{infer_query, InferTy, InferenceResult};
|
pub(crate) use infer::{infer_query, InferTy, InferenceResult};
|
||||||
pub use lower::CallableDef;
|
pub use lower::CallableDef;
|
||||||
pub(crate) use lower::{
|
pub(crate) use lower::{
|
||||||
callable_item_sig, generic_defaults, generic_predicates, type_for_def, type_for_field,
|
callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def,
|
||||||
TypableDef,
|
type_for_field, TypableDef,
|
||||||
};
|
};
|
||||||
pub(crate) use traits::ProjectionPredicate;
|
pub(crate) use traits::ProjectionPredicate;
|
||||||
|
|
||||||
|
|
|
@ -849,8 +849,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
|
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
|
||||||
if let Ty::Apply(a_ty) = callable_ty {
|
if let Ty::Apply(a_ty) = callable_ty {
|
||||||
if let TypeCtor::FnDef(def) = a_ty.ctor {
|
if let TypeCtor::FnDef(def) = a_ty.ctor {
|
||||||
|
let generic_predicates = self.db.generic_predicates(match def {
|
||||||
|
// TODO add helper function
|
||||||
|
CallableDef::Function(f) => f.into(),
|
||||||
|
CallableDef::Struct(s) => s.into(),
|
||||||
|
CallableDef::EnumVariant(_e) => unimplemented!(),
|
||||||
|
});
|
||||||
|
for predicate in generic_predicates.iter() {
|
||||||
|
let predicate = predicate.clone().subst(&a_ty.parameters);
|
||||||
|
if let Some(obligation) = Obligation::from_predicate(predicate) {
|
||||||
|
self.obligations.push(obligation);
|
||||||
|
}
|
||||||
|
}
|
||||||
// add obligation for trait implementation, if this is a trait method
|
// add obligation for trait implementation, if this is a trait method
|
||||||
// FIXME also register obligations from where clauses from the trait or impl and method
|
|
||||||
match def {
|
match def {
|
||||||
CallableDef::Function(f) => {
|
CallableDef::Function(f) => {
|
||||||
if let Some(trait_) = f.parent_trait(self.db) {
|
if let Some(trait_) = f.parent_trait(self.db) {
|
||||||
|
@ -992,7 +1003,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
(Vec::new(), Ty::Unknown)
|
(Vec::new(), Ty::Unknown)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// FIXME register obligations from where clauses from the function
|
self.register_obligations_for_call(&callee_ty);
|
||||||
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
||||||
for (arg, param) in args.iter().zip(param_iter) {
|
for (arg, param) in args.iter().zip(param_iter) {
|
||||||
self.infer_expr(*arg, &Expectation::has_type(param));
|
self.infer_expr(*arg, &Expectation::has_type(param));
|
||||||
|
|
|
@ -318,15 +318,13 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the where clause(s) of an item with generics.
|
/// Resolve the where clause(s) of an item with generics.
|
||||||
pub(crate) fn generic_predicates(
|
pub(crate) fn generic_predicates_query(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
def: GenericDef,
|
def: GenericDef,
|
||||||
) -> Arc<[GenericPredicate]> {
|
) -> Arc<[GenericPredicate]> {
|
||||||
let resolver = def.resolver(db);
|
let resolver = def.resolver(db);
|
||||||
let generic_params = def.generic_params(db);
|
let predicates = resolver
|
||||||
let predicates = generic_params
|
.where_predicates_in_scope()
|
||||||
.where_predicates
|
|
||||||
.iter()
|
|
||||||
.map(|pred| {
|
.map(|pred| {
|
||||||
TraitRef::for_where_predicate(db, &resolver, pred)
|
TraitRef::for_where_predicate(db, &resolver, pred)
|
||||||
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
||||||
|
@ -336,7 +334,7 @@ pub(crate) fn generic_predicates(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the default type params from generics
|
/// Resolve the default type params from generics
|
||||||
pub(crate) fn generic_defaults(db: &impl HirDatabase, def: GenericDef) -> Substs {
|
pub(crate) fn generic_defaults_query(db: &impl HirDatabase, def: GenericDef) -> Substs {
|
||||||
let resolver = def.resolver(db);
|
let resolver = def.resolver(db);
|
||||||
let generic_params = def.generic_params(db);
|
let generic_params = def.generic_params(db);
|
||||||
|
|
||||||
|
|
|
@ -2232,16 +2232,18 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[86; 87) 't': T
|
⋮
|
||||||
[92; 94) '{}': ()
|
⋮[86; 87) 't': T
|
||||||
[105; 144) '{ ...(s); }': ()
|
⋮[92; 94) '{}': ()
|
||||||
[115; 116) 's': S<{unknown}>
|
⋮[105; 144) '{ ...(s); }': ()
|
||||||
[119; 120) 'S': S<{unknown}>(T) -> S<T>
|
⋮[115; 116) 's': S<u32>
|
||||||
[119; 129) 'S(unknown)': S<{unknown}>
|
⋮[119; 120) 'S': S<u32>(T) -> S<T>
|
||||||
[121; 128) 'unknown': {unknown}
|
⋮[119; 129) 'S(unknown)': S<u32>
|
||||||
[135; 138) 'foo': fn foo<S<{unknown}>>(T) -> ()
|
⋮[121; 128) 'unknown': u32
|
||||||
[135; 141) 'foo(s)': ()
|
⋮[135; 138) 'foo': fn foo<S<u32>>(T) -> ()
|
||||||
[139; 140) 's': S<{unknown}>"###
|
⋮[135; 141) 'foo(s)': ()
|
||||||
|
⋮[139; 140) 's': S<u32>
|
||||||
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2259,17 +2261,19 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
[87; 88) 't': T
|
⋮
|
||||||
[98; 100) '{}': ()
|
⋮[87; 88) 't': T
|
||||||
[111; 163) '{ ...(s); }': ()
|
⋮[98; 100) '{}': ()
|
||||||
[121; 122) 's': S<{unknown}>
|
⋮[111; 163) '{ ...(s); }': ()
|
||||||
[125; 126) 'S': S<{unknown}>(T) -> S<T>
|
⋮[121; 122) 's': S<u32>
|
||||||
[125; 135) 'S(unknown)': S<{unknown}>
|
⋮[125; 126) 'S': S<u32>(T) -> S<T>
|
||||||
[127; 134) 'unknown': {unknown}
|
⋮[125; 135) 'S(unknown)': S<u32>
|
||||||
[145; 146) 'x': u32
|
⋮[127; 134) 'unknown': u32
|
||||||
[154; 157) 'foo': fn foo<u32, S<{unknown}>>(T) -> U
|
⋮[145; 146) 'x': u32
|
||||||
[154; 160) 'foo(s)': u32
|
⋮[154; 157) 'foo': fn foo<u32, S<u32>>(T) -> U
|
||||||
[158; 159) 's': S<{unknown}>"###
|
⋮[154; 160) 'foo(s)': u32
|
||||||
|
⋮[158; 159) 's': S<u32>
|
||||||
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2822,6 +2826,94 @@ fn test(s: S) {
|
||||||
assert_eq!(t, "{unknown}");
|
assert_eq!(t, "{unknown}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn obligation_from_function_clause() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
trait Trait<T> {}
|
||||||
|
impl Trait<u32> for S {}
|
||||||
|
|
||||||
|
fn foo<T: Trait<U>, U>(t: T) -> U {}
|
||||||
|
|
||||||
|
fn test(s: S) {
|
||||||
|
foo(s)<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "u32");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn obligation_from_method_clause() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
trait Trait<T> {}
|
||||||
|
impl Trait<isize> for S {}
|
||||||
|
|
||||||
|
struct O;
|
||||||
|
impl O {
|
||||||
|
fn foo<T: Trait<U>, U>(&self, t: T) -> U {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
O.foo(S)<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "isize");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn obligation_from_self_method_clause() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
trait Trait<T> {}
|
||||||
|
impl Trait<i64> for S {}
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
fn foo<U>(&self) -> U where Self: Trait<U> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
S.foo()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "i64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn obligation_from_impl_clause() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
trait Trait<T> {}
|
||||||
|
impl Trait<&str> for S {}
|
||||||
|
|
||||||
|
struct O<T>;
|
||||||
|
impl<U, T: Trait<U>> O<T> {
|
||||||
|
fn foo(&self) -> U {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(o: O<S>) {
|
||||||
|
o.foo()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "&str");
|
||||||
|
}
|
||||||
|
|
||||||
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
||||||
let file = db.parse(pos.file_id).ok().unwrap();
|
let file = db.parse(pos.file_id).ok().unwrap();
|
||||||
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
||||||
|
|
|
@ -7,7 +7,7 @@ use parking_lot::Mutex;
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use super::{Canonical, ProjectionTy, TraitRef, Ty};
|
use super::{Canonical, GenericPredicate, ProjectionTy, TraitRef, Ty};
|
||||||
use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
|
use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
|
||||||
|
|
||||||
use self::chalk::{from_chalk, ToChalk};
|
use self::chalk::{from_chalk, ToChalk};
|
||||||
|
@ -78,6 +78,15 @@ pub enum Obligation {
|
||||||
// Projection(ProjectionPredicate),
|
// Projection(ProjectionPredicate),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Obligation {
|
||||||
|
pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
|
||||||
|
match predicate {
|
||||||
|
GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
|
||||||
|
GenericPredicate::Error => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ProjectionPredicate {
|
pub struct ProjectionPredicate {
|
||||||
pub projection_ty: ProjectionTy,
|
pub projection_ty: ProjectionTy,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue