From 16a7d8cc850002b427fdc8d21ccde81caaed7902 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 13 Aug 2019 23:09:08 +0200 Subject: Add `impl Trait` and `dyn Trait` types - refactor bounds handling in the AST a bit - add HIR for bounds - add `Ty::Dyn` and `Ty::Opaque` variants and lower `dyn Trait` / `impl Trait` syntax to them --- crates/ra_hir/src/generics.rs | 20 ++---- crates/ra_hir/src/ty.rs | 120 ++++++++++++++++++++++++++++++--- crates/ra_hir/src/ty/lower.rs | 65 +++++++++++++++--- crates/ra_hir/src/ty/tests.rs | 120 +++++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 4 +- crates/ra_hir/src/type_ref.rs | 51 ++++++++++++-- crates/ra_syntax/src/ast.rs | 2 +- crates/ra_syntax/src/ast/extensions.rs | 26 +++++++ 8 files changed, 366 insertions(+), 42 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index e75337cdf..d6728cc9f 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -11,7 +11,7 @@ use crate::{ db::{AstDatabase, DefDatabase, HirDatabase}, name::SELF_TYPE, path::Path, - type_ref::TypeRef, + type_ref::{TypeBound, TypeRef}, AdtDef, AsName, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct, Trait, TypeAlias, Union, }; @@ -35,10 +35,12 @@ pub struct GenericParams { /// 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. +/// It might still result in multiple actual predicates though, because of +/// associated type bindings like `Iterator`. #[derive(Clone, PartialEq, Eq, Debug)] pub struct WherePredicate { pub(crate) type_ref: TypeRef, - pub(crate) trait_ref: Path, + pub(crate) bound: TypeBound, } // FIXME: consts can have type parameters from their parents (i.e. associated consts of traits) @@ -143,18 +145,8 @@ impl GenericParams { // FIXME: remove this bound return; } - let path = bound - .type_ref() - .and_then(|tr| match tr { - ast::TypeRef::PathType(path) => path.path(), - _ => None, - }) - .and_then(Path::from_ast); - let path = match path { - Some(p) => p, - None => return, - }; - self.where_predicates.push(WherePredicate { type_ref, trait_ref: path }); + let bound = TypeBound::from_ast(bound); + self.where_predicates.push(WherePredicate { type_ref, bound }); } pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 642dd02cb..4e5bdbae4 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -161,14 +161,28 @@ pub enum Ty { name: Name, }, - /// A bound type variable. Only used during trait resolution to represent - /// Chalk variables. + /// A bound type variable. Used during trait resolution to represent Chalk + /// variables, and in `Dyn` and `Opaque` bounds to represent the `Self` type. Bound(u32), /// A type variable used during type checking. Not to be confused with a /// type parameter. Infer(InferTy), + /// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust). + /// + /// The predicates are quantified over the `Self` type, i.e. `Ty::Bound(0)` + /// represents the `Self` type inside the bounds. This is currently + /// implicit; Chalk has the `Binders` struct to make it explicit, but it + /// didn't seem worth the overhead yet. + Dyn(Arc<[GenericPredicate]>), + + /// An opaque type (`impl Trait`). + /// + /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for + /// more. + Opaque(Arc<[GenericPredicate]>), + /// A placeholder for a type which could not be computed; this is propagated /// to avoid useless error messages. Doubles as a placeholder where type /// variables are inserted before type checking, since we want to try to @@ -194,6 +208,12 @@ impl Substs { Substs(self.0.iter().cloned().take(n).collect::>().into()) } + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { + for t in self.0.iter() { + t.walk(f); + } + } + pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { // Without an Arc::make_mut_slice, we can't avoid the clone here: let mut v: Vec<_> = self.0.iter().cloned().collect(); @@ -270,6 +290,14 @@ impl TraitRef { }); self } + + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { + self.substs.walk(f); + } + + pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + self.substs.walk_mut(f); + } } /// Like `generics::WherePredicate`, but with resolved types: A condition on the @@ -299,6 +327,20 @@ impl GenericPredicate { GenericPredicate::Error => self, } } + + pub fn walk(&self, f: &mut impl FnMut(&Ty)) { + match self { + GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f), + GenericPredicate::Error => {} + } + } + + pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { + match self { + GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f), + GenericPredicate::Error => {} + } + } } /// Basically a claim (currently not validated / checked) that the contained @@ -386,6 +428,11 @@ impl Ty { t.walk(f); } } + Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + for p in predicates.iter() { + p.walk(f); + } + } Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); @@ -402,6 +449,13 @@ impl Ty { Ty::UnselectedProjection(p_ty) => { p_ty.parameters.walk_mut(f); } + Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + let mut v: Vec<_> = predicates.iter().cloned().collect(); + for p in &mut v { + p.walk_mut(f); + } + *predicates = v.into(); + } Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); @@ -669,6 +723,28 @@ impl HirDisplay for Ty { Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?, Ty::Param { name, .. } => write!(f, "{}", name)?, Ty::Bound(idx) => write!(f, "?{}", idx)?, + Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + match self { + Ty::Dyn(_) => write!(f, "dyn ")?, + Ty::Opaque(_) => write!(f, "impl ")?, + _ => unreachable!(), + }; + // looping by hand here just to format the bounds in a slightly nicer way + let mut first = true; + for p in predicates.iter() { + if !first { + write!(f, " + ")?; + } + first = false; + match p { + // don't show the $0 self type + GenericPredicate::Implemented(trait_ref) => { + trait_ref.hir_fmt_ext(f, false)? + } + GenericPredicate::Error => p.hir_fmt(f)?, + } + } + } Ty::Unknown => write!(f, "{{unknown}}")?, Ty::Infer(..) => write!(f, "_")?, } @@ -676,14 +752,16 @@ impl HirDisplay for Ty { } } -impl HirDisplay for TraitRef { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - write!( - f, - "{}: {}", - self.substs[0].display(f.db), - self.trait_.name(f.db).unwrap_or_else(Name::missing) - )?; +impl TraitRef { + fn hir_fmt_ext( + &self, + f: &mut HirFormatter, + with_self_ty: bool, + ) -> fmt::Result { + if with_self_ty { + write!(f, "{}: ", self.substs[0].display(f.db),)?; + } + write!(f, "{}", self.trait_.name(f.db).unwrap_or_else(Name::missing))?; if self.substs.len() > 1 { write!(f, "<")?; f.write_joined(&self.substs[1..], ", ")?; @@ -693,6 +771,28 @@ impl HirDisplay for TraitRef { } } +impl HirDisplay for TraitRef { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + self.hir_fmt_ext(f, true) + } +} + +impl HirDisplay for &GenericPredicate { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + HirDisplay::hir_fmt(*self, f) + } +} + +impl HirDisplay for GenericPredicate { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + match self { + GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, + GenericPredicate::Error => write!(f, "{{error}}")?, + } + Ok(()) + } +} + impl HirDisplay for Obligation { fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { match self { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index debedcbb8..47d161277 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -17,7 +17,7 @@ use crate::{ path::{GenericArg, PathSegment}, resolve::{Resolution, Resolver}, ty::AdtDef, - type_ref::TypeRef, + type_ref::{TypeBound, TypeRef}, BuiltinType, Const, Enum, EnumVariant, Function, HirDatabase, ModuleDef, Path, Static, Struct, StructField, Trait, TypeAlias, Union, }; @@ -58,6 +58,22 @@ impl Ty { let sig = Substs(inner_tys.into()); Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig) } + TypeRef::DynTrait(bounds) => { + let self_ty = Ty::Bound(0); + let predicates = bounds + .iter() + .map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone())) + .collect::>(); + Ty::Dyn(predicates.into()) + } + TypeRef::ImplTrait(bounds) => { + let self_ty = Ty::Bound(0); + let predicates = bounds + .iter() + .map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone())) + .collect::>(); + Ty::Opaque(predicates.into()) + } TypeRef::Error => Ty::Unknown, } } @@ -310,13 +326,46 @@ impl TraitRef { TraitRef { trait_, substs } } - pub(crate) fn for_where_predicate( + pub(crate) fn from_where_predicate( db: &impl HirDatabase, resolver: &Resolver, pred: &WherePredicate, ) -> Option { let self_ty = Ty::from_hir(db, resolver, &pred.type_ref); - TraitRef::from_path(db, resolver, &pred.trait_ref, Some(self_ty)) + TraitRef::from_type_bound(db, resolver, &pred.bound, self_ty) + } + + pub(crate) fn from_type_bound( + db: &impl HirDatabase, + resolver: &Resolver, + bound: &TypeBound, + self_ty: Ty, + ) -> Option { + match bound { + TypeBound::Path(path) => TraitRef::from_path(db, resolver, path, Some(self_ty)), + TypeBound::Error => None, + } + } +} + +impl GenericPredicate { + pub(crate) fn from_where_predicate( + db: &impl HirDatabase, + resolver: &Resolver, + where_predicate: &WherePredicate, + ) -> GenericPredicate { + TraitRef::from_where_predicate(db, &resolver, where_predicate) + .map_or(GenericPredicate::Error, GenericPredicate::Implemented) + } + + pub(crate) fn from_type_bound( + db: &impl HirDatabase, + resolver: &Resolver, + bound: &TypeBound, + self_ty: Ty, + ) -> GenericPredicate { + TraitRef::from_type_bound(db, &resolver, bound, self_ty) + .map_or(GenericPredicate::Error, GenericPredicate::Implemented) } } @@ -376,10 +425,7 @@ pub(crate) fn trait_env( ) -> Arc { let predicates = resolver .where_predicates_in_scope() - .map(|pred| { - TraitRef::for_where_predicate(db, &resolver, pred) - .map_or(GenericPredicate::Error, GenericPredicate::Implemented) - }) + .map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) .collect::>(); Arc::new(super::TraitEnvironment { predicates }) @@ -393,10 +439,7 @@ pub(crate) fn generic_predicates_query( let resolver = def.resolver(db); let predicates = resolver .where_predicates_in_scope() - .map(|pred| { - TraitRef::for_where_predicate(db, &resolver, pred) - .map_or(GenericPredicate::Error, GenericPredicate::Implemented) - }) + .map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) .collect::>(); predicates.into() } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 57fd5492d..93c62b0d4 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3273,6 +3273,126 @@ fn test(t: T) { assert_eq!(t, "{unknown}"); } +#[test] +fn impl_trait() { + assert_snapshot_matches!( + infer(r#" +trait Trait { + fn foo(&self) -> T; +} +fn bar() -> impl Trait {} + +fn test(x: impl Trait, y: &impl Trait) { + x; + y; + let z = bar(); + x.foo(); + y.foo(); + z.foo(); +} +"#), + @r###" + ⋮ + ⋮[30; 34) 'self': &Self + ⋮[72; 74) '{}': () + ⋮[84; 85) 'x': impl Trait + ⋮[104; 105) 'y': &impl Trait + ⋮[125; 200) '{ ...o(); }': () + ⋮[131; 132) 'x': impl Trait + ⋮[138; 139) 'y': &impl Trait + ⋮[149; 150) 'z': impl Trait + ⋮[153; 156) 'bar': fn bar() -> impl Trait + ⋮[153; 158) 'bar()': impl Trait + ⋮[164; 165) 'x': impl Trait + ⋮[164; 171) 'x.foo()': {unknown} + ⋮[177; 178) 'y': &impl Trait + ⋮[177; 184) 'y.foo()': {unknown} + ⋮[190; 191) 'z': impl Trait + ⋮[190; 197) 'z.foo()': {unknown} + "### + ); +} + +#[test] +fn dyn_trait() { + assert_snapshot_matches!( + infer(r#" +trait Trait { + fn foo(&self) -> T; +} +fn bar() -> dyn Trait {} + +fn test(x: dyn Trait, y: &dyn Trait) { + x; + y; + let z = bar(); + x.foo(); + y.foo(); + z.foo(); +} +"#), + @r###" + ⋮ + ⋮[30; 34) 'self': &Self + ⋮[71; 73) '{}': () + ⋮[83; 84) 'x': dyn Trait + ⋮[102; 103) 'y': &dyn Trait + ⋮[122; 197) '{ ...o(); }': () + ⋮[128; 129) 'x': dyn Trait + ⋮[135; 136) 'y': &dyn Trait + ⋮[146; 147) 'z': dyn Trait + ⋮[150; 153) 'bar': fn bar() -> dyn Trait + ⋮[150; 155) 'bar()': dyn Trait + ⋮[161; 162) 'x': dyn Trait + ⋮[161; 168) 'x.foo()': {unknown} + ⋮[174; 175) 'y': &dyn Trait + ⋮[174; 181) 'y.foo()': {unknown} + ⋮[187; 188) 'z': dyn Trait + ⋮[187; 194) 'z.foo()': {unknown} + "### + ); +} + +#[test] +fn dyn_trait_bare() { + assert_snapshot_matches!( + infer(r#" +trait Trait { + fn foo(&self) -> u64; +} +fn bar() -> Trait {} + +fn test(x: Trait, y: &Trait) -> u64 { + x; + y; + let z = bar(); + x.foo(); + y.foo(); + z.foo(); +} +"#), + @r###" + ⋮ + ⋮[27; 31) 'self': &Self + ⋮[61; 63) '{}': () + ⋮[73; 74) 'x': {unknown} + ⋮[83; 84) 'y': &{unknown} + ⋮[101; 176) '{ ...o(); }': () + ⋮[107; 108) 'x': {unknown} + ⋮[114; 115) 'y': &{unknown} + ⋮[125; 126) 'z': {unknown} + ⋮[129; 132) 'bar': fn bar() -> {unknown} + ⋮[129; 134) 'bar()': {unknown} + ⋮[140; 141) 'x': {unknown} + ⋮[140; 147) 'x.foo()': {unknown} + ⋮[153; 154) 'y': &{unknown} + ⋮[153; 160) 'y.foo()': {unknown} + ⋮[166; 167) 'z': {unknown} + ⋮[166; 173) 'z.foo()': {unknown} + "### + ); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 6df7094c5..2ebc06135 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -80,7 +80,9 @@ impl ToChalk for Ty { // FIXME this is clearly incorrect, but probably not too incorrect // and I'm not sure what to actually do with Ty::Unknown // maybe an alternative would be `for T`? (meaningless in rust, but expressible in chalk's Ty) - Ty::Unknown => { + // + // FIXME also dyn and impl Trait are currently handled like Unknown because Chalk doesn't have them yet + Ty::Unknown | Ty::Dyn(_) | Ty::Opaque(_) => { PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::max_value() }.to_ty() } } diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index b92a0b55a..949fa7a2c 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs @@ -1,7 +1,7 @@ //! HIR for references to types. Paths in these are not yet resolved. They can //! be directly created from an ast::TypeRef, without further queries. -use ra_syntax::ast::{self, TypeAscriptionOwner}; +use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner}; use crate::Path; @@ -49,8 +49,16 @@ pub enum TypeRef { /// A fn pointer. Last element of the vector is the return type. Fn(Vec), // For - // ImplTrait, - // DynTrait, + ImplTrait(Vec), + DynTrait(Vec), + Error, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum TypeBound { + Path(Path), + // also for<> bounds + // also Lifetimes Error, } @@ -95,8 +103,12 @@ impl TypeRef { } // for types are close enough for our purposes to the inner type for now... ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()), - ast::TypeRef::ImplTraitType(_inner) => TypeRef::Error, - ast::TypeRef::DynTraitType(_inner) => TypeRef::Error, + ast::TypeRef::ImplTraitType(inner) => { + TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list())) + } + ast::TypeRef::DynTraitType(inner) => { + TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list())) + } } } @@ -112,3 +124,32 @@ impl TypeRef { TypeRef::Tuple(Vec::new()) } } + +pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option) -> Vec { + if let Some(type_bounds) = type_bounds_opt { + type_bounds.bounds().map(TypeBound::from_ast).collect() + } else { + vec![] + } +} + +impl TypeBound { + pub(crate) fn from_ast(node: ast::TypeBound) -> Self { + match node.kind() { + Some(ast::TypeBoundKind::PathType(path_type)) => { + let path = match path_type.path() { + Some(p) => p, + None => return TypeBound::Error, + }; + let path = match Path::from_ast(path) { + Some(p) => p, + None => return TypeBound::Error, + }; + TypeBound::Path(path) + } + Some(ast::TypeBoundKind::ForType(_)) | Some(ast::TypeBoundKind::Lifetime(_)) | None => { + TypeBound::Error + } + } + } +} diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 6f0489617..afdfca66e 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -15,7 +15,7 @@ use crate::{ pub use self::{ expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp}, - extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind}, + extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, generated::*, tokens::*, traits::*, diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index efe261fc2..8de979ca2 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -399,3 +399,29 @@ impl ast::TraitDef { self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) } } + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TypeBoundKind { + /// Trait + PathType(ast::PathType), + /// for<'a> ... + ForType(ast::ForType), + /// 'a + Lifetime(ast::SyntaxToken), +} + +impl ast::TypeBound { + pub fn kind(&self) -> Option { + let child = self.syntax.first_child_or_token()?; + match child.kind() { + PATH_TYPE => Some(TypeBoundKind::PathType( + ast::PathType::cast(child.into_node().unwrap()).unwrap(), + )), + FOR_TYPE => Some(TypeBoundKind::ForType( + ast::ForType::cast(child.into_node().unwrap()).unwrap(), + )), + LIFETIME => Some(TypeBoundKind::Lifetime(child.into_token().unwrap())), + _ => unreachable!(), + } + } +} -- cgit v1.2.3 From b1a40042e8f595af0486cf1cc70b63be1ff302b3 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 22 Aug 2019 13:23:50 +0200 Subject: Handle impl/dyn Trait in method resolution When we have one of these, the `Trait` doesn't need to be in scope to call its methods. So we need to consider this when looking for method candidates. (Actually I think the same is true when we have a bound `T: some::Trait`, but we don't handle that yet). At the same time, since Chalk doesn't handle these types yet, add a small hack to skip Chalk in method resolution and just consider `impl Trait: Trait` always true. This is enough to e.g. get completions for `impl Trait`, but since we don't do any unification we won't infer the return type of e.g. `impl Into::into()`. --- crates/ra_hir/src/ty.rs | 13 +++++ crates/ra_hir/src/ty/method_resolution.rs | 16 +++++- crates/ra_hir/src/ty/tests.rs | 82 ++++++++++++++++++++----------- 3 files changed, 79 insertions(+), 32 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 4e5bdbae4..b54c80318 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -583,6 +583,19 @@ impl Ty { ty => ty, }) } + + /// If this is an `impl Trait` or `dyn Trait`, returns that trait. + pub fn inherent_trait(&self) -> Option { + match self { + Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + predicates.iter().find_map(|pred| match pred { + GenericPredicate::Implemented(tr) => Some(tr.trait_), + _ => None, + }) + } + _ => None, + } + } } impl HirDisplay for &Ty { diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 3f4c43aee..9873a0440 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -211,12 +211,19 @@ fn iterate_trait_method_candidates( let krate = resolver.krate()?; // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) let env = lower::trait_env(db, resolver); - 'traits: for t in resolver.traits_in_scope(db) { + // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope + let traits = ty.value.inherent_trait().into_iter().chain(resolver.traits_in_scope(db)); + 'traits: for t in traits { let data = t.trait_data(db); + + // FIXME this is a bit of a hack, since Chalk should say the same thing + // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet + let inherently_implemented = ty.value.inherent_trait() == Some(t); + // we'll be lazy about checking whether the type implements the // trait, but if we find out it doesn't, we'll skip the rest of the // iteration - let mut known_implemented = false; + let mut known_implemented = inherently_implemented; for item in data.items() { if let TraitItem::Function(m) = *item { let data = m.data(db); @@ -271,6 +278,11 @@ pub(crate) fn implements_trait( krate: Crate, trait_: Trait, ) -> bool { + if ty.value.inherent_trait() == Some(trait_) { + // FIXME this is a bit of a hack, since Chalk should say the same thing + // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet + return true; + } let env = lower::trait_env(db, resolver); let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone()); let solution = db.trait_solve(krate, goal); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 93c62b0d4..41cea9564 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3279,6 +3279,7 @@ fn impl_trait() { infer(r#" trait Trait { fn foo(&self) -> T; + fn foo2(&self) -> i64; } fn bar() -> impl Trait {} @@ -3289,26 +3290,36 @@ fn test(x: impl Trait, y: &impl Trait) { x.foo(); y.foo(); z.foo(); + x.foo2(); + y.foo2(); + z.foo2(); } "#), @r###" ⋮ ⋮[30; 34) 'self': &Self - ⋮[72; 74) '{}': () - ⋮[84; 85) 'x': impl Trait - ⋮[104; 105) 'y': &impl Trait - ⋮[125; 200) '{ ...o(); }': () - ⋮[131; 132) 'x': impl Trait - ⋮[138; 139) 'y': &impl Trait - ⋮[149; 150) 'z': impl Trait - ⋮[153; 156) 'bar': fn bar() -> impl Trait - ⋮[153; 158) 'bar()': impl Trait - ⋮[164; 165) 'x': impl Trait - ⋮[164; 171) 'x.foo()': {unknown} - ⋮[177; 178) 'y': &impl Trait - ⋮[177; 184) 'y.foo()': {unknown} - ⋮[190; 191) 'z': impl Trait - ⋮[190; 197) 'z.foo()': {unknown} + ⋮[55; 59) 'self': &Self + ⋮[99; 101) '{}': () + ⋮[111; 112) 'x': impl Trait + ⋮[131; 132) 'y': &impl Trait + ⋮[152; 269) '{ ...2(); }': () + ⋮[158; 159) 'x': impl Trait + ⋮[165; 166) 'y': &impl Trait + ⋮[176; 177) 'z': impl Trait + ⋮[180; 183) 'bar': fn bar() -> impl Trait + ⋮[180; 185) 'bar()': impl Trait + ⋮[191; 192) 'x': impl Trait + ⋮[191; 198) 'x.foo()': {unknown} + ⋮[204; 205) 'y': &impl Trait + ⋮[204; 211) 'y.foo()': {unknown} + ⋮[217; 218) 'z': impl Trait + ⋮[217; 224) 'z.foo()': {unknown} + ⋮[230; 231) 'x': impl Trait + ⋮[230; 238) 'x.foo2()': i64 + ⋮[244; 245) 'y': &impl Trait + ⋮[244; 252) 'y.foo2()': i64 + ⋮[258; 259) 'z': impl Trait + ⋮[258; 266) 'z.foo2()': i64 "### ); } @@ -3319,6 +3330,7 @@ fn dyn_trait() { infer(r#" trait Trait { fn foo(&self) -> T; + fn foo2(&self) -> i64; } fn bar() -> dyn Trait {} @@ -3329,26 +3341,36 @@ fn test(x: dyn Trait, y: &dyn Trait) { x.foo(); y.foo(); z.foo(); + x.foo2(); + y.foo2(); + z.foo2(); } "#), @r###" ⋮ ⋮[30; 34) 'self': &Self - ⋮[71; 73) '{}': () - ⋮[83; 84) 'x': dyn Trait - ⋮[102; 103) 'y': &dyn Trait - ⋮[122; 197) '{ ...o(); }': () - ⋮[128; 129) 'x': dyn Trait - ⋮[135; 136) 'y': &dyn Trait - ⋮[146; 147) 'z': dyn Trait - ⋮[150; 153) 'bar': fn bar() -> dyn Trait - ⋮[150; 155) 'bar()': dyn Trait - ⋮[161; 162) 'x': dyn Trait - ⋮[161; 168) 'x.foo()': {unknown} - ⋮[174; 175) 'y': &dyn Trait - ⋮[174; 181) 'y.foo()': {unknown} - ⋮[187; 188) 'z': dyn Trait - ⋮[187; 194) 'z.foo()': {unknown} + ⋮[55; 59) 'self': &Self + ⋮[98; 100) '{}': () + ⋮[110; 111) 'x': dyn Trait + ⋮[129; 130) 'y': &dyn Trait + ⋮[149; 266) '{ ...2(); }': () + ⋮[155; 156) 'x': dyn Trait + ⋮[162; 163) 'y': &dyn Trait + ⋮[173; 174) 'z': dyn Trait + ⋮[177; 180) 'bar': fn bar() -> dyn Trait + ⋮[177; 182) 'bar()': dyn Trait + ⋮[188; 189) 'x': dyn Trait + ⋮[188; 195) 'x.foo()': {unknown} + ⋮[201; 202) 'y': &dyn Trait + ⋮[201; 208) 'y.foo()': {unknown} + ⋮[214; 215) 'z': dyn Trait + ⋮[214; 221) 'z.foo()': {unknown} + ⋮[227; 228) 'x': dyn Trait + ⋮[227; 235) 'x.foo2()': i64 + ⋮[241; 242) 'y': &dyn Trait + ⋮[241; 249) 'y.foo2()': i64 + ⋮[255; 256) 'z': dyn Trait + ⋮[255; 263) 'z.foo2()': i64 "### ); } -- cgit v1.2.3 From 4768f5e7177159b894d65a50b1e4492cb4048ac3 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 22 Aug 2019 17:43:09 +0200 Subject: Improve/fix type bound lowering --- crates/ra_hir/src/ty/tests.rs | 33 ++++++++++++++++++++ crates/ra_hir/src/type_ref.rs | 6 ++-- crates/ra_syntax/src/ast/extensions.rs | 55 ++++++++++++++++++---------------- 3 files changed, 64 insertions(+), 30 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 41cea9564..c5818b738 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3415,6 +3415,39 @@ fn test(x: Trait, y: &Trait) -> u64 { ); } +#[test] +fn weird_bounds() { + assert_snapshot_matches!( + infer(r#" +trait Trait {} +fn test() { + let a: impl Trait + 'lifetime = foo; + let b: impl 'lifetime = foo; + let b: impl (Trait) = foo; + let b: impl ('lifetime) = foo; + let d: impl ?Sized = foo; + let e: impl Trait + ?Sized = foo; +} +"#), + @r###" + ⋮ + ⋮[26; 237) '{ ...foo; }': () + ⋮[36; 37) 'a': impl Trait + {error} + ⋮[64; 67) 'foo': impl Trait + {error} + ⋮[77; 78) 'b': impl {error} + ⋮[97; 100) 'foo': impl {error} + ⋮[110; 111) 'b': impl Trait + ⋮[128; 131) 'foo': impl Trait + ⋮[141; 142) 'b': impl {error} + ⋮[163; 166) 'foo': impl {error} + ⋮[176; 177) 'd': impl {error} + ⋮[193; 196) 'foo': impl {error} + ⋮[206; 207) 'e': impl Trait + {error} + ⋮[231; 234) 'foo': impl Trait + {error} + "### + ); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index 949fa7a2c..fa91bfb22 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs @@ -136,7 +136,7 @@ pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option) impl TypeBound { pub(crate) fn from_ast(node: ast::TypeBound) -> Self { match node.kind() { - Some(ast::TypeBoundKind::PathType(path_type)) => { + ast::TypeBoundKind::PathType(path_type) => { let path = match path_type.path() { Some(p) => p, None => return TypeBound::Error, @@ -147,9 +147,7 @@ impl TypeBound { }; TypeBound::Path(path) } - Some(ast::TypeBoundKind::ForType(_)) | Some(ast::TypeBoundKind::Lifetime(_)) | None => { - TypeBound::Error - } + ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error, } } } diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 8de979ca2..e0ea3e5ab 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -382,7 +382,36 @@ impl ast::WherePred { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TypeBoundKind { + /// Trait + PathType(ast::PathType), + /// for<'a> ... + ForType(ast::ForType), + /// 'a + Lifetime(ast::SyntaxToken), +} + impl ast::TypeBound { + pub fn kind(&self) -> TypeBoundKind { + if let Some(path_type) = children(self).next() { + TypeBoundKind::PathType(path_type) + } else if let Some(for_type) = children(self).next() { + TypeBoundKind::ForType(for_type) + } else if let Some(lifetime) = self.lifetime() { + TypeBoundKind::Lifetime(lifetime) + } else { + unreachable!() + } + } + + fn lifetime(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == LIFETIME) + } + pub fn question_mark_token(&self) -> Option { self.syntax() .children_with_tokens() @@ -399,29 +428,3 @@ impl ast::TraitDef { self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) } } - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum TypeBoundKind { - /// Trait - PathType(ast::PathType), - /// for<'a> ... - ForType(ast::ForType), - /// 'a - Lifetime(ast::SyntaxToken), -} - -impl ast::TypeBound { - pub fn kind(&self) -> Option { - let child = self.syntax.first_child_or_token()?; - match child.kind() { - PATH_TYPE => Some(TypeBoundKind::PathType( - ast::PathType::cast(child.into_node().unwrap()).unwrap(), - )), - FOR_TYPE => Some(TypeBoundKind::ForType( - ast::ForType::cast(child.into_node().unwrap()).unwrap(), - )), - LIFETIME => Some(TypeBoundKind::Lifetime(child.into_token().unwrap())), - _ => unreachable!(), - } - } -} -- cgit v1.2.3 From 18c7a1ebe7ced9d8039132da06dcd954affec2eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 22 Aug 2019 21:08:34 +0200 Subject: Make sysroot use `RUST_SRC_PATH` if set --- crates/ra_project_model/src/sysroot.rs | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'crates') diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 2a7927f7e..0c27d4f4b 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs @@ -1,4 +1,5 @@ use std::{ + env, path::{Path, PathBuf}, process::Command, }; @@ -33,21 +34,13 @@ impl Sysroot { } pub fn discover(cargo_toml: &Path) -> Result { - let rustc_output = Command::new("rustc") - .current_dir(cargo_toml.parent().unwrap()) - .args(&["--print", "sysroot"]) - .output()?; - if !rustc_output.status.success() { - Err("failed to locate sysroot")? - } - let stdout = String::from_utf8(rustc_output.stdout)?; - let sysroot_path = Path::new(stdout.trim()); - let src = sysroot_path.join("lib/rustlib/src/rust/src"); + let src = try_find_src_path(cargo_toml)?; + if !src.exists() { Err(format!( "can't load standard library from sysroot\n\ {:?}\n\ - try running `rustup component add rust-src`", + try running `rustup component add rust-src` or set `RUST_SRC_PATH`", src, ))?; } @@ -83,6 +76,23 @@ impl Sysroot { } } +fn try_find_src_path(cargo_toml: &Path) -> Result { + if let Ok(path) = env::var("RUST_SRC_PATH") { + return Ok(path.into()); + } + + let rustc_output = Command::new("rustc") + .current_dir(cargo_toml.parent().unwrap()) + .args(&["--print", "sysroot"]) + .output()?; + if !rustc_output.status.success() { + Err("failed to locate sysroot")?; + } + let stdout = String::from_utf8(rustc_output.stdout)?; + let sysroot_path = Path::new(stdout.trim()); + Ok(sysroot_path.join("lib/rustlib/src/rust/src")) +} + impl SysrootCrate { pub fn name(self, sysroot: &Sysroot) -> &str { &sysroot.crates[self].name -- cgit v1.2.3