Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 34 additions & 30 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,10 +1148,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ignore_import: Option<Import<'ra>>,
) -> Result<Decl<'ra>, ControlFlow<Determinacy, Determinacy>> {
let key = BindingKey::new(ident, ns);
let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span);
let resolution = resolution_ref.borrow();
let resolution = self.resolution(module.to_module(), key);

let binding = resolution.non_glob_decl.filter(|b| Some(*b) != ignore_decl);
let binding =
resolution.as_ref().and_then(|r| r.non_glob_decl).filter(|b| Some(*b) != ignore_decl);

if let Some(finalize) = finalize {
return self.get_mut().finalize_module_binding(
Expand All @@ -1170,21 +1170,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) };
}

// We need to detect resolution cycles to avoid infinite recursion. The guard ensures
// the resolution is removed when this resolve call ends.
let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref)
.map_err(|_| ControlFlow::Continue(Determined))?;
if let Some(resolution) = resolution {
// We need to detect resolution cycles to avoid infinite recursion. The guard ensures
// the resolution is removed when this resolve call ends.
let _cycle_guard = cycle_detection::enter_cycle_detector(module, key)
.map_err(|_| ControlFlow::Continue(Determined))?;

// Check if one of single imports can still define the name, block if it can.
if self.reborrow().single_import_can_define_name(
&resolution,
None,
ns,
ignore_import,
ignore_decl,
parent_scope,
) {
return Err(ControlFlow::Break(Undetermined));
// Check if one of single imports can still define the name, block if it can.
if self.reborrow().single_import_can_define_name(
&resolution,
None,
ns,
ignore_import,
ignore_decl,
parent_scope,
) {
return Err(ControlFlow::Break(Undetermined));
}
}

// Check if one of unexpanded macros can still define the name.
Expand All @@ -1210,10 +1212,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ignore_import: Option<Import<'ra>>,
) -> Result<Decl<'ra>, ControlFlow<Determinacy, Determinacy>> {
let key = BindingKey::new(ident, ns);
let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span);
let resolution = resolution_ref.borrow();
let resolution = self.resolution(module.to_module(), key);

let binding = resolution.glob_decl.filter(|b| Some(*b) != ignore_decl);
let binding =
resolution.as_ref().and_then(|r| r.glob_decl).filter(|b| Some(*b) != ignore_decl);

if let Some(finalize) = finalize {
return self.get_mut().finalize_module_binding(
Expand All @@ -1228,20 +1230,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {

// We need to detect resolution cycles to avoid infinite recursion. The guard ensures
// the resolution is removed when this resolve call ends.
let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref)
let _cycle_guard = cycle_detection::enter_cycle_detector(module, key)
.map_err(|_| ControlFlow::Continue(Determined))?;

// Check if one of single imports can still define the name,
// if it can then our result is not determined and can be invalidated.
if self.reborrow().single_import_can_define_name(
&resolution,
binding,
ns,
ignore_import,
ignore_decl,
parent_scope,
) {
return Err(ControlFlow::Break(Undetermined));
if let Some(resolution) = resolution {
if self.reborrow().single_import_can_define_name(
&resolution,
binding,
ns,
ignore_import,
ignore_decl,
parent_scope,
) {
return Err(ControlFlow::Break(Undetermined));
}
}

// So we have a resolution that's from a glob import. This resolution is determined
Expand Down
18 changes: 10 additions & 8 deletions compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,23 +326,23 @@ impl<'ra> NameResolution<'ra> {
pub(crate) mod cycle_detection {
use std::ptr;

use crate::CacheRefCell;
use crate::imports::NameResolutionRef;
use crate::{BindingKey, CacheRefCell, LocalModule};

thread_local!(
/// During import resolution, recursive imports can form cycles.
/// This set stores the active resolution stack for the current thread.
/// So it's essentially a recursion stack.
/// By keeping track of the module and `BindingKey` pair that identifies
/// the specific resolution.
///
/// The key is the interned address of a `RefCell<NameResolution<'ra>>` allocated
/// The pointer is the interned address of a `Interned<'ra, ModuleData>` allocated
/// in the `Resolver Arenas` (lifetime `'ra`), it is thus stable and allows casting
/// to a `*const ()` for comparison. This is done because we can't use lifetimes
/// other than `'static` in thread local storage.
static ACTIVE_RESOLUTIONS: CacheRefCell<Vec<*const ()>> = Default::default();
static ACTIVE_RESOLUTIONS: CacheRefCell<Vec<(*const (), BindingKey)>> = Default::default();
);

pub(crate) struct ActiveResolutionGuard {
key: *const (),
key: (*const (), BindingKey),
}

impl Drop for ActiveResolutionGuard {
Expand All @@ -360,9 +360,11 @@ pub(crate) mod cycle_detection {
/// Returns `Err(())` if a cycle is detected, otherwise this returns a
/// guard that will remove the resolution when dropped.
pub(crate) fn enter_cycle_detector<'ra>(
resolution: NameResolutionRef<'ra>,
module: LocalModule<'ra>,
binding_key: BindingKey,
) -> Result<ActiveResolutionGuard, ()> {
let key = ptr::from_ref(resolution.0).cast::<()>();
let module_key = ptr::from_ref(module.0.0).cast();
let key = (module_key, binding_key);
ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| {
if ar.contains(&key) {
return Err(());
Expand Down
Loading