From a2b6734ea40dcd53e1bdad6d8dc3e31c0fec929a Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 13 Mar 2019 14:24:42 -0700 Subject: [PATCH 1/2] resolve: collect trait aliases along with traits --- src/librustc_resolve/lib.rs | 15 ++++++++++-- .../run-pass/traits/trait-alias-import.rs | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/traits/trait-alias-import.rs diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index ac149be4b2a8..e50cda602c2b 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1195,6 +1195,13 @@ impl<'a> ModuleData<'a> { } } + fn is_trait_alias(&self) -> bool { + match self.kind { + ModuleKind::Def(Def::TraitAlias(_), _) => true, + _ => false, + } + } + fn nearest_item_scope(&'a self) -> Module<'a> { if self.is_trait() { self.parent.unwrap() } else { self } } @@ -4369,8 +4376,10 @@ impl<'a> Resolver<'a> { let mut collected_traits = Vec::new(); module.for_each_child(|name, ns, binding| { if ns != TypeNS { return } - if let Def::Trait(_) = binding.def() { - collected_traits.push((name, binding)); + match binding.def() { + Def::Trait(_) | + Def::TraitAlias(_) => collected_traits.push((name, binding)), + _ => (), } }); *traits = Some(collected_traits.into_boxed_slice()); @@ -4834,6 +4843,7 @@ impl<'a> Resolver<'a> { let container = match parent.kind { ModuleKind::Def(Def::Mod(_), _) => "module", ModuleKind::Def(Def::Trait(_), _) => "trait", + ModuleKind::Def(Def::TraitAlias(_), _) => "trait alias", ModuleKind::Block(..) => "block", _ => "enum", }; @@ -4862,6 +4872,7 @@ impl<'a> Resolver<'a> { (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", (TypeNS, Some(module)) if module.is_normal() => "module", (TypeNS, Some(module)) if module.is_trait() => "trait", + (TypeNS, Some(module)) if module.is_trait_alias() => "trait alias", (TypeNS, _) => "type", }; diff --git a/src/test/run-pass/traits/trait-alias-import.rs b/src/test/run-pass/traits/trait-alias-import.rs new file mode 100644 index 000000000000..9def1f0d4804 --- /dev/null +++ b/src/test/run-pass/traits/trait-alias-import.rs @@ -0,0 +1,23 @@ +#![feature(trait_alias)] + +mod inner { + pub trait Foo { + fn foo(&self); + } + + pub struct Qux; + + impl Foo for Qux { + fn foo(&self) {} + } + + pub trait Bar = Foo; +} + +// Import only the alias, not the `Foo` trait. +use inner::{Bar, Qux}; + +fn main() { + let q = Qux; + q.foo(); +} From 3ccd35cd70eed139de411f9533d406c498ed4d62 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Fri, 15 Mar 2019 14:55:04 -0700 Subject: [PATCH 2/2] resolve all in scope trait aliases, then elaborate their bounds --- src/librustc_resolve/lib.rs | 62 +++++++++++-------- src/librustc_resolve/resolve_imports.rs | 2 +- src/librustc_typeck/check/method/probe.rs | 46 +++++++++----- src/librustc_typeck/check/method/suggest.rs | 13 ++-- .../run-pass/traits/auxiliary/trait_alias.rs | 13 ++++ .../traits/trait-alias-import-cross-crate.rs | 14 +++++ .../run-pass/traits/trait-alias-import.rs | 17 ++++- src/test/ui/traits/trait-alias-ambiguous.rs | 24 +++++++ .../ui/traits/trait-alias-ambiguous.stderr | 20 ++++++ 9 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 src/test/run-pass/traits/auxiliary/trait_alias.rs create mode 100644 src/test/run-pass/traits/trait-alias-import-cross-crate.rs create mode 100644 src/test/ui/traits/trait-alias-ambiguous.rs create mode 100644 src/test/ui/traits/trait-alias-ambiguous.stderr diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e50cda602c2b..fdfb21a605f7 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1195,13 +1195,6 @@ impl<'a> ModuleData<'a> { } } - fn is_trait_alias(&self) -> bool { - match self.kind { - ModuleKind::Def(Def::TraitAlias(_), _) => true, - _ => false, - } - } - fn nearest_item_scope(&'a self) -> Module<'a> { if self.is_trait() { self.parent.unwrap() } else { self } } @@ -1359,7 +1352,7 @@ impl<'a> NameBinding<'a> { } } - // We sometimes need to treat variants as `pub` for backwards compatibility + // We sometimes need to treat variants as `pub` for backwards compatibility. fn pseudo_vis(&self) -> ty::Visibility { if self.is_variant() && self.def().def_id().is_local() { ty::Visibility::Public @@ -2717,7 +2710,7 @@ impl<'a> Resolver<'a> { { let mut self_type_rib = Rib::new(NormalRibKind); - // plain insert (no renaming, types are not currently hygienic....) + // Plain insert (no renaming, since types are not currently hygienic) self_type_rib.bindings.insert(keywords::SelfUpper.ident(), self_def); self.ribs[TypeNS].push(self_type_rib); f(self); @@ -4386,18 +4379,37 @@ impl<'a> Resolver<'a> { } for &(trait_name, binding) in traits.as_ref().unwrap().iter() { - let module = binding.module().unwrap(); - let mut ident = ident; - if ident.span.glob_adjust(module.expansion, binding.span.ctxt().modern()).is_none() { - continue - } - if self.resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - false, - module.span, - ).is_ok() { + // Traits have pseudo-modules that can be used to search for the given ident. + if let Some(module) = binding.module() { + let mut ident = ident; + if ident.span.glob_adjust( + module.expansion, + binding.span.ctxt().modern(), + ).is_none() { + continue + } + if self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + module.span, + ).is_ok() { + let import_id = match binding.kind { + NameBindingKind::Import { directive, .. } => { + self.maybe_unused_trait_imports.insert(directive.id); + self.add_to_glob_map(&directive, trait_name); + Some(directive.id) + } + _ => None, + }; + let trait_def_id = module.def_id().unwrap(); + found_traits.push(TraitCandidate { def_id: trait_def_id, import_id }); + } + } else if let Def::TraitAlias(_) = binding.def() { + // For now, just treat all trait aliases as possible candidates, since we don't + // know if the ident is somewhere in the transitive bounds. + let import_id = match binding.kind { NameBindingKind::Import { directive, .. } => { self.maybe_unused_trait_imports.insert(directive.id); @@ -4406,8 +4418,10 @@ impl<'a> Resolver<'a> { } _ => None, }; - let trait_def_id = module.def_id().unwrap(); - found_traits.push(TraitCandidate { def_id: trait_def_id, import_id: import_id }); + let trait_def_id = binding.def().def_id(); + found_traits.push(TraitCandidate { def_id: trait_def_id, import_id }); + } else { + bug!("candidate is not trait or trait alias?") } } } @@ -4843,7 +4857,6 @@ impl<'a> Resolver<'a> { let container = match parent.kind { ModuleKind::Def(Def::Mod(_), _) => "module", ModuleKind::Def(Def::Trait(_), _) => "trait", - ModuleKind::Def(Def::TraitAlias(_), _) => "trait alias", ModuleKind::Block(..) => "block", _ => "enum", }; @@ -4872,7 +4885,6 @@ impl<'a> Resolver<'a> { (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", (TypeNS, Some(module)) if module.is_normal() => "module", (TypeNS, Some(module)) if module.is_trait() => "trait", - (TypeNS, Some(module)) if module.is_trait_alias() => "trait alias", (TypeNS, _) => "type", }; diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 9daffd522bf2..05597de2735e 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -1245,7 +1245,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { self.populate_module_if_necessary(module); - if let Some(Def::Trait(_)) = module.def() { + if module.is_trait() { self.session.span_err(directive.span, "items in traits are not importable."); return; } else if module.def_id() == directive.parent_scope.module.def_id() { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 1f0ab3abb283..48fa26e5882f 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -895,20 +895,36 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let trait_substs = self.fresh_item_substs(trait_def_id); let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs); - for item in self.impl_or_trait_item(trait_def_id) { - // Check whether `trait_def_id` defines a method with suitable name: - if !self.has_applicable_self(&item) { - debug!("method has inapplicable self"); - self.record_static_candidate(TraitSource(trait_def_id)); - continue; - } + if self.tcx.is_trait_alias(trait_def_id) { + // For trait aliases, assume all super-traits are relevant. + let bounds = iter::once(trait_ref.to_poly_trait_ref()); + self.elaborate_bounds(bounds, |this, new_trait_ref, item| { + let new_trait_ref = this.erase_late_bound_regions(&new_trait_ref); + + let (xform_self_ty, xform_ret_ty) = + this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); + this.push_candidate(Candidate { + xform_self_ty, xform_ret_ty, item, import_id, + kind: TraitCandidate(new_trait_ref), + }, true); + }); + } else { + debug_assert!(self.tcx.is_trait(trait_def_id)); + for item in self.impl_or_trait_item(trait_def_id) { + // Check whether `trait_def_id` defines a method with suitable name. + if !self.has_applicable_self(&item) { + debug!("method has inapplicable self"); + self.record_static_candidate(TraitSource(trait_def_id)); + continue; + } - let (xform_self_ty, xform_ret_ty) = - self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); - self.push_candidate(Candidate { - xform_self_ty, xform_ret_ty, item, import_id, - kind: TraitCandidate(trait_ref), - }, false); + let (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); + self.push_candidate(Candidate { + xform_self_ty, xform_ret_ty, item, import_id, + kind: TraitCandidate(trait_ref), + }, false); + } } Ok(()) } @@ -929,7 +945,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { .filter(|&name| set.insert(name)) .collect(); - // sort them by the name so we have a stable result + // Sort them by the name so we have a stable result. names.sort_by_cached_key(|n| n.as_str()); names } @@ -944,6 +960,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { return r; } + debug!("pick: actual search failed, assemble diagnotics"); + let static_candidates = mem::replace(&mut self.static_candidates, vec![]); let private_candidate = self.private_candidate.take(); let unsatisfied_predicates = mem::replace(&mut self.unsatisfied_predicates, vec![]); diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index f933e61b8c63..b49614eedb15 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -748,9 +748,13 @@ fn compute_all_traits<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Vec impl<'v, 'a, 'tcx> itemlikevisit::ItemLikeVisitor<'v> for Visitor<'a, 'tcx> { fn visit_item(&mut self, i: &'v hir::Item) { - if let hir::ItemKind::Trait(..) = i.node { - let def_id = self.map.local_def_id_from_hir_id(i.hir_id); - self.traits.push(def_id); + match i.node { + hir::ItemKind::Trait(..) | + hir::ItemKind::TraitAlias(..) => { + let def_id = self.map.local_def_id_from_hir_id(i.hir_id); + self.traits.push(def_id); + } + _ => () } } @@ -772,7 +776,8 @@ fn compute_all_traits<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Vec external_mods: &mut FxHashSet, def: Def) { match def { - Def::Trait(def_id) => { + Def::Trait(def_id) | + Def::TraitAlias(def_id) => { traits.push(def_id); } Def::Mod(def_id) => { diff --git a/src/test/run-pass/traits/auxiliary/trait_alias.rs b/src/test/run-pass/traits/auxiliary/trait_alias.rs new file mode 100644 index 000000000000..9e412215512c --- /dev/null +++ b/src/test/run-pass/traits/auxiliary/trait_alias.rs @@ -0,0 +1,13 @@ +#![feature(trait_alias)] + +pub trait Hello { + fn hello(&self); +} + +pub struct Hi; + +impl Hello for Hi { + fn hello(&self) {} +} + +pub trait Greet = Hello; diff --git a/src/test/run-pass/traits/trait-alias-import-cross-crate.rs b/src/test/run-pass/traits/trait-alias-import-cross-crate.rs new file mode 100644 index 000000000000..975542ab49b5 --- /dev/null +++ b/src/test/run-pass/traits/trait-alias-import-cross-crate.rs @@ -0,0 +1,14 @@ +// run-pass +// aux-build:trait_alias.rs + +#![feature(trait_alias)] + +extern crate trait_alias; + +// Import only the alias, not the real trait. +use trait_alias::{Greet, Hi}; + +fn main() { + let hi = Hi; + hi.hello(); // From `Hello`, via `Greet` alias. +} diff --git a/src/test/run-pass/traits/trait-alias-import.rs b/src/test/run-pass/traits/trait-alias-import.rs index 9def1f0d4804..7d63320b9aad 100644 --- a/src/test/run-pass/traits/trait-alias-import.rs +++ b/src/test/run-pass/traits/trait-alias-import.rs @@ -14,10 +14,25 @@ mod inner { pub trait Bar = Foo; } +mod two { + pub trait A { + fn foo(); + } + + impl A for u8 { + fn foo() {} + } +} + // Import only the alias, not the `Foo` trait. use inner::{Bar, Qux}; +// Declaring an alias also brings in aliased methods. +trait Two = two::A; + fn main() { let q = Qux; - q.foo(); + q.foo(); // From Bar. + + u8::foo(); // From A. } diff --git a/src/test/ui/traits/trait-alias-ambiguous.rs b/src/test/ui/traits/trait-alias-ambiguous.rs new file mode 100644 index 000000000000..28409e0c6627 --- /dev/null +++ b/src/test/ui/traits/trait-alias-ambiguous.rs @@ -0,0 +1,24 @@ +#![feature(trait_alias)] + +mod inner { + pub trait A { fn foo(&self); } + pub trait B { fn foo(&self); } + + impl A for u8 { + fn foo(&self) {} + } + impl B for u8 { + fn foo(&self) {} + } + + pub trait C = A + B; +} + +use inner::C; + +fn main() { + let t = 1u8; + t.foo(); //~ ERROR E0034 + + inner::A::foo(&t); // ok +} diff --git a/src/test/ui/traits/trait-alias-ambiguous.stderr b/src/test/ui/traits/trait-alias-ambiguous.stderr new file mode 100644 index 000000000000..b7443269b882 --- /dev/null +++ b/src/test/ui/traits/trait-alias-ambiguous.stderr @@ -0,0 +1,20 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/trait-alias-ambiguous.rs:21:7 + | +LL | t.foo(); + | ^^^ multiple `foo` found + | +note: candidate #1 is defined in an impl of the trait `inner::A` for the type `u8` + --> $DIR/trait-alias-ambiguous.rs:8:9 + | +LL | fn foo(&self) {} + | ^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `inner::B` for the type `u8` + --> $DIR/trait-alias-ambiguous.rs:11:9 + | +LL | fn foo(&self) {} + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`.