Skip to content

Commit b7071b2

Browse files
committed
resolve: Simplify collection of traits in scope
1 parent 6526e5c commit b7071b2

File tree

7 files changed

+139
-161
lines changed

7 files changed

+139
-161
lines changed

compiler/rustc_resolve/src/build_reduced_graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl<'a> Resolver<'a> {
115115
self.get_module(parent_id)
116116
}
117117

118-
crate fn get_module(&mut self, def_id: DefId) -> Module<'a> {
118+
pub fn get_module(&mut self, def_id: DefId) -> Module<'a> {
119119
// If this is a local module, it will be in `module_map`, no need to recalculate it.
120120
if let Some(def_id) = def_id.as_local() {
121121
return self.module_map[&def_id];

compiler/rustc_resolve/src/late.rs

+10-62
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::{ResolutionError, Resolver, Segment, UseError};
1414
use rustc_ast::ptr::P;
1515
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
1616
use rustc_ast::*;
17-
use rustc_ast::{unwrap_or, walk_list};
1817
use rustc_ast_lowering::ResolverAstLowering;
1918
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2019
use rustc_errors::DiagnosticId;
@@ -1911,7 +1910,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
19111910
// it needs to be added to the trait map.
19121911
if ns == ValueNS {
19131912
let item_name = path.last().unwrap().ident;
1914-
let traits = self.get_traits_containing_item(item_name, ns);
1913+
let traits = self.traits_in_scope(item_name, ns);
19151914
self.r.trait_map.insert(id, traits);
19161915
}
19171916

@@ -2372,12 +2371,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
23722371
// field, we need to add any trait methods we find that match
23732372
// the field name so that we can do some nice error reporting
23742373
// later on in typeck.
2375-
let traits = self.get_traits_containing_item(ident, ValueNS);
2374+
let traits = self.traits_in_scope(ident, ValueNS);
23762375
self.r.trait_map.insert(expr.id, traits);
23772376
}
23782377
ExprKind::MethodCall(ref segment, ..) => {
23792378
debug!("(recording candidate traits for expr) recording traits for {}", expr.id);
2380-
let traits = self.get_traits_containing_item(segment.ident, ValueNS);
2379+
let traits = self.traits_in_scope(segment.ident, ValueNS);
23812380
self.r.trait_map.insert(expr.id, traits);
23822381
}
23832382
_ => {
@@ -2386,64 +2385,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
23862385
}
23872386
}
23882387

2389-
fn get_traits_containing_item(
2390-
&mut self,
2391-
mut ident: Ident,
2392-
ns: Namespace,
2393-
) -> Vec<TraitCandidate> {
2394-
debug!("(getting traits containing item) looking for '{}'", ident.name);
2395-
2396-
let mut found_traits = Vec::new();
2397-
// Look for the current trait.
2398-
if let Some((module, _)) = self.current_trait_ref {
2399-
if self
2400-
.r
2401-
.resolve_ident_in_module(
2402-
ModuleOrUniformRoot::Module(module),
2403-
ident,
2404-
ns,
2405-
&self.parent_scope,
2406-
false,
2407-
module.span,
2408-
)
2409-
.is_ok()
2410-
{
2411-
let def_id = module.def_id().unwrap();
2412-
found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] });
2413-
}
2414-
}
2415-
2416-
ident.span = ident.span.normalize_to_macros_2_0();
2417-
let mut search_module = self.parent_scope.module;
2418-
loop {
2419-
self.r.get_traits_in_module_containing_item(
2420-
ident,
2421-
ns,
2422-
search_module,
2423-
&mut found_traits,
2424-
&self.parent_scope,
2425-
);
2426-
let mut span_data = ident.span.data();
2427-
search_module = unwrap_or!(
2428-
self.r.hygienic_lexical_parent(search_module, &mut span_data.ctxt),
2429-
break
2430-
);
2431-
ident.span = span_data.span();
2432-
}
2433-
2434-
if let Some(prelude) = self.r.prelude {
2435-
if !search_module.no_implicit_prelude {
2436-
self.r.get_traits_in_module_containing_item(
2437-
ident,
2438-
ns,
2439-
prelude,
2440-
&mut found_traits,
2441-
&self.parent_scope,
2442-
);
2443-
}
2444-
}
2445-
2446-
found_traits
2388+
fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> Vec<TraitCandidate> {
2389+
self.r.traits_in_scope(
2390+
self.current_trait_ref.as_ref().map(|(module, _)| *module),
2391+
&self.parent_scope,
2392+
ident.span.ctxt(),
2393+
Some((ident.name, ns)),
2394+
)
24472395
}
24482396
}
24492397

compiler/rustc_resolve/src/lib.rs

+63-64
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ use rustc_index::vec::IndexVec;
4444
use rustc_metadata::creader::{CStore, CrateLoader};
4545
use rustc_middle::hir::exports::ExportMap;
4646
use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn};
47+
use rustc_middle::span_bug;
4748
use rustc_middle::ty::query::Providers;
4849
use rustc_middle::ty::{self, DefIdTree, ResolverOutputs};
49-
use rustc_middle::{bug, span_bug};
5050
use rustc_session::lint;
5151
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
5252
use rustc_session::Session;
@@ -1477,49 +1477,76 @@ impl<'a> Resolver<'a> {
14771477
self.crate_loader.postprocess(krate);
14781478
}
14791479

1480-
fn get_traits_in_module_containing_item(
1480+
pub fn traits_in_scope(
1481+
&mut self,
1482+
current_trait: Option<Module<'a>>,
1483+
parent_scope: &ParentScope<'a>,
1484+
ctxt: SyntaxContext,
1485+
assoc_item: Option<(Symbol, Namespace)>,
1486+
) -> Vec<TraitCandidate> {
1487+
let mut found_traits = Vec::new();
1488+
1489+
if let Some(module) = current_trait {
1490+
if self.trait_may_have_item(Some(module), assoc_item) {
1491+
let def_id = module.def_id().unwrap();
1492+
found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] });
1493+
}
1494+
}
1495+
1496+
self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| {
1497+
match scope {
1498+
Scope::Module(module) => {
1499+
this.traits_in_module(module, assoc_item, &mut found_traits);
1500+
}
1501+
Scope::StdLibPrelude => {
1502+
if let Some(module) = this.prelude {
1503+
this.traits_in_module(module, assoc_item, &mut found_traits);
1504+
}
1505+
}
1506+
Scope::ExternPrelude | Scope::ToolPrelude | Scope::BuiltinTypes => {}
1507+
_ => unreachable!(),
1508+
}
1509+
None::<()>
1510+
});
1511+
1512+
found_traits
1513+
}
1514+
1515+
fn traits_in_module(
14811516
&mut self,
1482-
ident: Ident,
1483-
ns: Namespace,
14841517
module: Module<'a>,
1518+
assoc_item: Option<(Symbol, Namespace)>,
14851519
found_traits: &mut Vec<TraitCandidate>,
1486-
parent_scope: &ParentScope<'a>,
14871520
) {
1488-
assert!(ns == TypeNS || ns == ValueNS);
14891521
module.ensure_traits(self);
14901522
let traits = module.traits.borrow();
1523+
for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() {
1524+
if self.trait_may_have_item(trait_binding.module(), assoc_item) {
1525+
let def_id = trait_binding.res().def_id();
1526+
let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name);
1527+
found_traits.push(TraitCandidate { def_id, import_ids });
1528+
}
1529+
}
1530+
}
14911531

1492-
for &(trait_name, binding) in traits.as_ref().unwrap().iter() {
1493-
// Traits have pseudo-modules that can be used to search for the given ident.
1494-
if let Some(module) = binding.module() {
1495-
let mut ident = ident;
1496-
if ident.span.glob_adjust(module.expansion, binding.span).is_none() {
1497-
continue;
1498-
}
1499-
if self
1500-
.resolve_ident_in_module_unadjusted(
1501-
ModuleOrUniformRoot::Module(module),
1502-
ident,
1503-
ns,
1504-
parent_scope,
1505-
false,
1506-
module.span,
1507-
)
1508-
.is_ok()
1509-
{
1510-
let import_ids = self.find_transitive_imports(&binding.kind, trait_name);
1511-
let trait_def_id = module.def_id().unwrap();
1512-
found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids });
1513-
}
1514-
} else if let Res::Def(DefKind::TraitAlias, _) = binding.res() {
1515-
// For now, just treat all trait aliases as possible candidates, since we don't
1516-
// know if the ident is somewhere in the transitive bounds.
1517-
let import_ids = self.find_transitive_imports(&binding.kind, trait_name);
1518-
let trait_def_id = binding.res().def_id();
1519-
found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids });
1520-
} else {
1521-
bug!("candidate is not trait or trait alias?")
1532+
// List of traits in scope is pruned on best effort basis. We reject traits not having an
1533+
// associated item with the given name and namespace (if specified). This is a conservative
1534+
// optimization, proper hygienic type-based resolution of associated items is done in typeck.
1535+
// We don't reject trait aliases (`trait_module == None`) because we don't have access to their
1536+
// associated items.
1537+
fn trait_may_have_item(
1538+
&mut self,
1539+
trait_module: Option<Module<'a>>,
1540+
assoc_item: Option<(Symbol, Namespace)>,
1541+
) -> bool {
1542+
match (trait_module, assoc_item) {
1543+
(Some(trait_module), Some((name, ns))) => {
1544+
self.resolutions(trait_module).borrow().iter().any(|resolution| {
1545+
let (&BindingKey { ident: assoc_ident, ns: assoc_ns, .. }, _) = resolution;
1546+
assoc_ns == ns && assoc_ident.name == name
1547+
})
15221548
}
1549+
_ => true,
15231550
}
15241551
}
15251552

@@ -3227,34 +3254,6 @@ impl<'a> Resolver<'a> {
32273254
})
32283255
}
32293256

3230-
/// This is equivalent to `get_traits_in_module_containing_item`, but without filtering by the associated item.
3231-
///
3232-
/// This is used by rustdoc for intra-doc links.
3233-
pub fn traits_in_scope(&mut self, module_id: DefId) -> Vec<TraitCandidate> {
3234-
let module = self.get_module(module_id);
3235-
module.ensure_traits(self);
3236-
let traits = module.traits.borrow();
3237-
let to_candidate =
3238-
|this: &mut Self, &(trait_name, binding): &(Ident, &NameBinding<'_>)| TraitCandidate {
3239-
def_id: binding.res().def_id(),
3240-
import_ids: this.find_transitive_imports(&binding.kind, trait_name),
3241-
};
3242-
3243-
let mut candidates: Vec<_> =
3244-
traits.as_ref().unwrap().iter().map(|x| to_candidate(self, x)).collect();
3245-
3246-
if let Some(prelude) = self.prelude {
3247-
if !module.no_implicit_prelude {
3248-
prelude.ensure_traits(self);
3249-
candidates.extend(
3250-
prelude.traits.borrow().as_ref().unwrap().iter().map(|x| to_candidate(self, x)),
3251-
);
3252-
}
3253-
}
3254-
3255-
candidates
3256-
}
3257-
32583257
/// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>`
32593258
/// isn't something that can be returned because it can't be made to live that long,
32603259
/// and also it's a private type. Fortunately rustdoc doesn't need to know the error,

src/librustdoc/passes/collect_intra_doc_links.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_session::lint::{
1919
builtin::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS},
2020
Lint,
2121
};
22-
use rustc_span::hygiene::MacroKind;
22+
use rustc_span::hygiene::{MacroKind, SyntaxContext};
2323
use rustc_span::symbol::Ident;
2424
use rustc_span::symbol::Symbol;
2525
use rustc_span::DUMMY_SP;
@@ -770,7 +770,12 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx
770770
let mut cache = cx.module_trait_cache.borrow_mut();
771771
let in_scope_traits = cache.entry(module).or_insert_with(|| {
772772
cx.enter_resolver(|resolver| {
773-
resolver.traits_in_scope(module).into_iter().map(|candidate| candidate.def_id).collect()
773+
let parent_scope = &ParentScope::module(resolver.get_module(module), resolver);
774+
resolver
775+
.traits_in_scope(None, parent_scope, SyntaxContext::root(), None)
776+
.into_iter()
777+
.map(|candidate| candidate.def_id)
778+
.collect()
774779
})
775780
});
776781

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Macros with def-site hygiene still bring traits into scope.
2+
// It is not clear whether this is desirable behavior or not.
3+
// It is also not clear how to prevent it if it is not desirable.
4+
5+
// check-pass
6+
7+
#![feature(decl_macro)]
8+
#![feature(trait_alias)]
9+
10+
mod traits {
11+
pub trait Trait1 {
12+
fn simple_import(&self) {}
13+
}
14+
pub trait Trait2 {
15+
fn renamed_import(&self) {}
16+
}
17+
pub trait Trait3 {
18+
fn underscore_import(&self) {}
19+
}
20+
pub trait Trait4 {
21+
fn trait_alias(&self) {}
22+
}
23+
24+
impl Trait1 for () {}
25+
impl Trait2 for () {}
26+
impl Trait3 for () {}
27+
impl Trait4 for () {}
28+
}
29+
30+
macro m1() {
31+
use traits::Trait1;
32+
}
33+
macro m2() {
34+
use traits::Trait2 as Alias;
35+
}
36+
macro m3() {
37+
use traits::Trait3 as _;
38+
}
39+
macro m4() {
40+
trait Alias = traits::Trait4;
41+
}
42+
43+
fn main() {
44+
m1!();
45+
m2!();
46+
m3!();
47+
m4!();
48+
49+
().simple_import();
50+
().renamed_import();
51+
().underscore_import();
52+
().trait_alias();
53+
}

src/test/ui/underscore-imports/hygiene.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
// Make sure that underscore imports have the same hygiene considerations as
2-
// other imports.
1+
// Make sure that underscore imports have the same hygiene considerations as other imports.
2+
3+
// check-pass
34

45
#![feature(decl_macro)]
56

67
mod x {
78
pub use std::ops::Deref as _;
89
}
910

10-
1111
macro glob_import() {
1212
pub use crate::x::*;
1313
}
@@ -35,6 +35,6 @@ fn main() {
3535
use crate::z::*;
3636
glob_import!();
3737
underscore_import!();
38-
(&()).deref(); //~ ERROR no method named `deref`
39-
(&mut ()).deref_mut(); //~ ERROR no method named `deref_mut`
38+
(&()).deref();
39+
(&mut ()).deref_mut();
4040
}

0 commit comments

Comments
 (0)