1182: Add HIR for where clauses & ignore impls with where clauses in trait resolution r=matklad a=flodiebold

This prevents any `impl<T> Trait for T where ...` from being treated as a
blanket impl while we don't handle where clauses yet.


Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2019-04-21 13:01:48 +00:00
commit 5e1c29543c
3 changed files with 63 additions and 5 deletions

View file

@ -5,11 +5,11 @@
use std::sync::Arc;
use ra_syntax::ast::{self, NameOwner, TypeParamsOwner};
use ra_syntax::ast::{self, NameOwner, TypeParamsOwner, TypeBoundsOwner};
use crate::{
db::DefDatabase,
Name, AsName, Function, Struct, Enum, Trait, TypeAlias, ImplBlock, Container
Name, AsName, Function, Struct, Enum, Trait, TypeAlias, ImplBlock, Container, path::Path, type_ref::TypeRef
};
/// Data about a generic parameter (to a function, struct, impl, ...).
@ -25,6 +25,15 @@ pub struct GenericParam {
pub struct GenericParams {
pub(crate) parent_params: Option<Arc<GenericParams>>,
pub(crate) params: Vec<GenericParam>,
pub(crate) where_predicates: Vec<WherePredicate>,
}
/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
/// 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,
}
// FIXME: consts can have type parameters from their parents (i.e. associated consts of traits)
@ -73,6 +82,9 @@ impl GenericParams {
if let Some(params) = node.type_param_list() {
self.fill_params(params, start)
}
if let Some(where_clause) = node.where_clause() {
self.fill_where_predicates(where_clause);
}
}
fn fill_params(&mut self, params: &ast::TypeParamList, start: u32) {
@ -83,6 +95,32 @@ impl GenericParams {
}
}
fn fill_where_predicates(&mut self, where_clause: &ast::WhereClause) {
for pred in where_clause.predicates() {
let type_ref = match pred.type_ref() {
Some(type_ref) => type_ref,
None => continue,
};
for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
let path = bound
.type_ref()
.and_then(|tr| match tr.kind() {
ast::TypeRefKind::PathType(path) => path.path(),
_ => None,
})
.and_then(Path::from_ast);
let path = match path {
Some(p) => p,
None => continue,
};
self.where_predicates.push(WherePredicate {
type_ref: TypeRef::from_ast(type_ref),
trait_ref: path,
});
}
}
}
pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> {
self.params.iter().find(|p| &p.name == name)
}

View file

@ -2477,6 +2477,23 @@ fn test() { (&S).foo()<|>; }
assert_eq!(t, "u128");
}
#[test]
fn method_resolution_where_clause_not_met() {
// The blanket impl shouldn't apply because we can't prove S: Clone
let t = type_at(
r#"
//- /main.rs
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 } }
fn test() { (&S).foo()<|>; }
"#,
);
assert_eq!(t, "i8");
}
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
let file = db.parse(pos.file_id);
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();

View file

@ -1,8 +1,8 @@
//! Stuff that will probably mostly replaced by Chalk.
use std::collections::HashMap;
use crate::db::HirDatabase;
use super::{ TraitRef, Substs, infer::{ TypeVarId, InferTy}, Ty};
use crate::{db::HirDatabase, generics::HasGenericParams};
use super::{TraitRef, Substs, infer::{TypeVarId, InferTy}, Ty};
// Copied (and simplified) from Chalk
@ -59,7 +59,10 @@ pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> Option<S
None => return None,
};
let crate_impl_blocks = db.impls_in_crate(krate);
let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_);
let mut impl_blocks = crate_impl_blocks
.lookup_impl_blocks_for_trait(&trait_ref.trait_)
// we don't handle where clauses at all, waiting for Chalk for that
.filter(|impl_block| impl_block.generic_params(db).where_predicates.is_empty());
impl_blocks
.find_map(|impl_block| unify_trait_refs(&trait_ref, &impl_block.target_trait_ref(db)?))
}