-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Resolver: Batched Import Resolution #145108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
77f5e5c
75c497c
9aa347e
71b10cd
803cc87
eda3931
3378f79
7e4b8d8
94bb516
64d7519
4a2a0dc
efb6489
fd413a3
74c18d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
use std::cell::Cell; | ||
use std::mem; | ||
use std::ops::Deref; | ||
|
||
use rustc_ast::NodeId; | ||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; | ||
|
@@ -41,6 +42,89 @@ use crate::{ | |
|
||
type Res = def::Res<NodeId>; | ||
|
||
pub(crate) struct ImportResolver<'r, 'ra, 'tcx> { | ||
r: CmResolver<'r, 'ra, 'tcx>, // always immutable | ||
batch: Vec<Import<'ra>>, // a.k.a. indeterminate_imports, also treated as output | ||
|
||
// outputs | ||
determined_imports: Vec<Import<'ra>>, | ||
glob_imports: Vec<Import<'ra>>, | ||
import_bindings: PerNS<Vec<(Module<'ra>, Import<'ra>, PendingBinding<'ra>)>>, | ||
} | ||
|
||
struct ImportResolutionOutputs<'ra> { | ||
petrochenkov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
indeterminate_imports: Vec<Import<'ra>>, | ||
determined_imports: Vec<Import<'ra>>, | ||
glob_imports: Vec<Import<'ra>>, | ||
import_bindings: PerNS<Vec<(Module<'ra>, Import<'ra>, PendingBinding<'ra>)>>, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest restructuring this structure like this: struct ImportResolutionOutputs {
indeterminate_imports: Vec<Import>,
determined_imports: Vec<(Import, SideEffect)>,
} where Then side effects will be applied in the same order in which the imports are resolved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, nice. That sounds better indeed, thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This way the diagnostics will also go in the source code order in common case, which is nice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And we would thus not need those changes to the |
||
|
||
impl<'r, 'ra, 'tcx> ImportResolver<'r, 'ra, 'tcx> { | ||
LorrensP-2158466 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub(crate) fn new(cmr: CmResolver<'r, 'ra, 'tcx>, batch: Vec<Import<'ra>>) -> Self { | ||
ImportResolver { | ||
r: cmr, | ||
batch, | ||
determined_imports: Vec::new(), | ||
glob_imports: Vec::new(), | ||
import_bindings: PerNS::default(), | ||
} | ||
} | ||
|
||
fn into_outputs(self) -> ImportResolutionOutputs<'ra> { | ||
ImportResolutionOutputs { | ||
indeterminate_imports: self.batch, | ||
determined_imports: self.determined_imports, | ||
glob_imports: self.glob_imports, | ||
import_bindings: self.import_bindings, | ||
} | ||
} | ||
} | ||
|
||
impl<'ra> ImportResolutionOutputs<'ra> { | ||
fn commit<'tcx>(self, r: &mut Resolver<'ra, 'tcx>) { | ||
r.indeterminate_imports = self.indeterminate_imports; | ||
r.determined_imports.extend(self.determined_imports); | ||
for glob in self.glob_imports { | ||
r.resolve_glob_import(glob); | ||
} | ||
|
||
for (ns, import_bindings) in self.import_bindings.into_iter_with() { | ||
for (parent, import, pending_binding) in import_bindings { | ||
let ImportKind::Single { target, ref bindings, .. } = import.kind else { | ||
unreachable!(); | ||
}; | ||
match pending_binding { | ||
PendingBinding::Ready(Some(binding)) => { | ||
r.define_binding_local(parent, target, ns, binding); | ||
} | ||
PendingBinding::Ready(None) => { | ||
let key = BindingKey::new(target, ns); | ||
r.update_local_resolution(parent, key, false, |_, resolution| { | ||
resolution.single_imports.swap_remove(&import); | ||
}); | ||
} | ||
_ => {} | ||
} | ||
bindings[ns].set(pending_binding); | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl<'r, 'ra, 'tcx> Deref for ImportResolver<'r, 'ra, 'tcx> { | ||
type Target = Resolver<'ra, 'tcx>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
self.r.deref() | ||
} | ||
} | ||
|
||
impl<'r, 'ra, 'tcx> AsRef<Resolver<'ra, 'tcx>> for ImportResolver<'r, 'ra, 'tcx> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now it is convenient to have the |
||
fn as_ref(&self) -> &Resolver<'ra, 'tcx> { | ||
self.r.as_ref() | ||
} | ||
} | ||
|
||
/// A [`NameBinding`] in the process of being resolved. | ||
#[derive(Clone, Copy, Default, PartialEq)] | ||
pub(crate) enum PendingBinding<'ra> { | ||
|
@@ -552,22 +636,32 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | |
/// Resolves all imports for the crate. This method performs the fixed- | ||
/// point iteration. | ||
pub(crate) fn resolve_imports(&mut self) { | ||
self.assert_speculative = true; | ||
let mut prev_indeterminate_count = usize::MAX; | ||
let mut indeterminate_count = self.indeterminate_imports.len() * 3; | ||
while indeterminate_count < prev_indeterminate_count { | ||
self.assert_speculative = true; | ||
prev_indeterminate_count = indeterminate_count; | ||
indeterminate_count = 0; | ||
for import in mem::take(&mut self.indeterminate_imports) { | ||
let import_indeterminate_count = self.cm().resolve_import(import); | ||
indeterminate_count += import_indeterminate_count; | ||
match import_indeterminate_count { | ||
0 => self.determined_imports.push(import), | ||
_ => self.indeterminate_imports.push(import), | ||
} | ||
let batch = mem::take(&mut self.indeterminate_imports); | ||
let (outputs, count) = ImportResolver::new(self.cm(), batch).resolve_batch(); | ||
LorrensP-2158466 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
indeterminate_count = count; | ||
self.assert_speculative = false; | ||
outputs.commit(self); | ||
} | ||
} | ||
|
||
fn resolve_batch<'r>( | ||
mut self: ImportResolver<'r, 'ra, 'tcx>, | ||
) -> (ImportResolutionOutputs<'ra>, usize) { | ||
let mut indeterminate_count = 0; | ||
for import in mem::take(&mut self.batch) { | ||
let import_indeterminate_count = self.resolve_import(import); | ||
indeterminate_count += import_indeterminate_count; | ||
match import_indeterminate_count { | ||
0 => self.determined_imports.push(import), | ||
_ => self.batch.push(import), | ||
} | ||
} | ||
self.assert_speculative = false; | ||
(self.into_outputs(), indeterminate_count) | ||
} | ||
|
||
pub(crate) fn finalize_imports(&mut self) { | ||
|
@@ -840,7 +934,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | |
/// | ||
/// Meanwhile, if resolve successful, the resolved bindings are written | ||
/// into the module. | ||
fn resolve_import<'r>(mut self: CmResolver<'r, 'ra, 'tcx>, import: Import<'ra>) -> usize { | ||
fn resolve_import<'r>(self: &mut ImportResolver<'r, 'ra, 'tcx>, import: Import<'ra>) -> usize { | ||
debug!( | ||
"(resolving import for module) resolving import `{}::...` in `{}`", | ||
Segment::names_to_string(&import.module_path), | ||
|
@@ -849,7 +943,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | |
let module = if let Some(module) = import.imported_module.get() { | ||
module | ||
} else { | ||
let path_res = self.reborrow().maybe_resolve_path( | ||
let path_res = self.r.reborrow().maybe_resolve_path( | ||
&import.module_path, | ||
None, | ||
&import.parent_scope, | ||
|
@@ -863,22 +957,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | |
} | ||
}; | ||
|
||
// FIXME: isolate if not a cache. | ||
import.imported_module.set(Some(module)); | ||
let (source, target, bindings, type_ns_only) = match import.kind { | ||
ImportKind::Single { source, target, ref bindings, type_ns_only, .. } => { | ||
(source, target, bindings, type_ns_only) | ||
} | ||
ImportKind::Glob { .. } => { | ||
// FIXME: Use mutable resolver directly as a hack, this should be an output of | ||
// specualtive resolution. | ||
self.get_mut_unchecked().resolve_glob_import(import); | ||
self.glob_imports.push(import); | ||
return 0; | ||
} | ||
_ => unreachable!(), | ||
}; | ||
|
||
let mut indeterminate_count = 0; | ||
self.per_ns_cm(|this, ns| { | ||
self.r.reborrow().per_ns_cm(|this, ns| { | ||
if !type_ns_only || ns == TypeNS { | ||
if bindings[ns].get() != PendingBinding::Pending { | ||
return; | ||
|
@@ -891,7 +984,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | |
Some(import), | ||
); | ||
let parent = import.parent_scope.module; | ||
let binding = match binding_result { | ||
let pending_binding = match binding_result { | ||
Ok(binding) => { | ||
if binding.is_assoc_item() | ||
&& !this.tcx.features().import_trait_associated_functions() | ||
|
@@ -906,39 +999,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | |
} | ||
// We need the `target`, `source` can be extracted. | ||
let imported_binding = this.import(binding, import); | ||
// FIXME: Use mutable resolver directly as a hack, this should be an output of | ||
// specualtive resolution. | ||
this.get_mut_unchecked().define_binding_local( | ||
parent, | ||
target, | ||
ns, | ||
imported_binding, | ||
); | ||
PendingBinding::Ready(Some(imported_binding)) | ||
} | ||
Err(Determinacy::Determined) => { | ||
// Don't remove underscores from `single_imports`, they were never added. | ||
if target.name != kw::Underscore { | ||
let key = BindingKey::new(target, ns); | ||
// FIXME: Use mutable resolver directly as a hack, this should be an output of | ||
// specualtive resolution. | ||
this.get_mut_unchecked().update_local_resolution( | ||
parent, | ||
key, | ||
false, | ||
|_, resolution| { | ||
resolution.single_imports.swap_remove(&import); | ||
}, | ||
); | ||
if target.name == kw::Underscore { | ||
return; | ||
} | ||
PendingBinding::Ready(None) | ||
} | ||
Err(Determinacy::Undetermined) => { | ||
indeterminate_count += 1; | ||
PendingBinding::Pending | ||
return; | ||
} | ||
}; | ||
bindings[ns].set(binding); | ||
self.import_bindings[ns].push((parent, import, pending_binding)); | ||
} | ||
}); | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.