From 787fb3e5ec56494f3856bb1ce33cbe296265d199 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 21 Apr 2019 14:21:58 +0200 Subject: Add HIR for where clauses & ignore impls with where clauses in trait resolution This prevents any `impl Trait for T where ...` from being treated as a blanket impl while we don't handle where clauses yet. --- crates/ra_hir/src/generics.rs | 42 ++++++++++++++++++++++++++++++++++++++++-- crates/ra_hir/src/ty/tests.rs | 17 +++++++++++++++++ crates/ra_hir/src/ty/traits.rs | 9 ++++++--- 3 files changed, 63 insertions(+), 5 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 5625c2459..1c71e21ea 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -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>, pub(crate) params: Vec, + pub(crate) where_predicates: Vec, +} + +/// 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) } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 86f18b487..a4c99528d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -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 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::(file.syntax(), pos.offset).unwrap(); diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index f8c3958bd..06f483899 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -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 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)?)) } -- cgit v1.2.3