Skip to content

resolve: Introduce RibKind::Block #145065

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

Merged
merged 4 commits into from
Aug 15, 2025
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
82 changes: 40 additions & 42 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let normalized_ident = Ident { span: normalized_span, ..ident };

// Walk backwards up the ribs in scope.
let mut module = self.graph_root;
for (i, rib) in ribs.iter().enumerate().rev() {
debug!("walk rib\n{:?}", rib.bindings);
// Use the rib kind to determine whether we are resolving parameters
Expand All @@ -334,51 +333,47 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
*original_rib_ident_def,
ribs,
)));
} else if let RibKind::Block(Some(module)) = rib.kind
&& let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted(
ModuleOrUniformRoot::Module(module),
ident,
ns,
parent_scope,
Shadowing::Unrestricted,
finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
ignore_binding,
None,
)
{
// The ident resolves to an item in a block.
return Some(LexicalScopeBinding::Item(binding));
} else if let RibKind::Module(module) = rib.kind {
// Encountered a module item, abandon ribs and look into that module and preludes.
return self
.cm()
.early_resolve_ident_in_lexical_scope(
orig_ident,
ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
parent_scope,
finalize,
finalize.is_some(),
ignore_binding,
None,
)
.ok()
.map(LexicalScopeBinding::Item);
}

module = match rib.kind {
RibKind::Module(module) => module,
RibKind::MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => {
// If an invocation of this macro created `ident`, give up on `ident`
// and switch to `ident`'s source from the macro definition.
ident.span.remove_mark();
continue;
}
_ => continue,
};

match module.kind {
ModuleKind::Block => {} // We can see through blocks
_ => break,
}

let item = self.cm().resolve_ident_in_module_unadjusted(
ModuleOrUniformRoot::Module(module),
ident,
ns,
parent_scope,
Shadowing::Unrestricted,
finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
ignore_binding,
None,
);
if let Ok(binding) = item {
// The ident resolves to an item.
return Some(LexicalScopeBinding::Item(binding));
if let RibKind::MacroDefinition(def) = rib.kind
&& def == self.macro_def(ident.span.ctxt())
{
// If an invocation of this macro created `ident`, give up on `ident`
// and switch to `ident`'s source from the macro definition.
ident.span.remove_mark();
}
}
self.cm()
.early_resolve_ident_in_lexical_scope(
orig_ident,
ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
parent_scope,
finalize,
finalize.is_some(),
ignore_binding,
None,
)
.ok()
.map(LexicalScopeBinding::Item)

unreachable!()
}

/// Resolve an identifier in lexical scope.
Expand Down Expand Up @@ -1171,6 +1166,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
for rib in ribs {
match rib.kind {
RibKind::Normal
| RibKind::Block(..)
| RibKind::FnOrCoroutine
| RibKind::Module(..)
| RibKind::MacroDefinition(..)
Expand Down Expand Up @@ -1263,6 +1259,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
for rib in ribs {
let (has_generic_params, def_kind) = match rib.kind {
RibKind::Normal
| RibKind::Block(..)
| RibKind::FnOrCoroutine
| RibKind::Module(..)
| RibKind::MacroDefinition(..)
Expand Down Expand Up @@ -1356,6 +1353,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
for rib in ribs {
let (has_generic_params, def_kind) = match rib.kind {
RibKind::Normal
| RibKind::Block(..)
| RibKind::FnOrCoroutine
| RibKind::Module(..)
| RibKind::MacroDefinition(..)
Expand Down
77 changes: 35 additions & 42 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ pub(crate) enum RibKind<'ra> {
/// No restriction needs to be applied.
Normal,

/// We passed through an `ast::Block`.
/// Behaves like `Normal`, but also partially like `Module` if the block contains items.
/// `Block(None)` must be always processed in the same way as `Block(Some(module))`
/// with empty `module`. The module can be `None` only because creation of some definitely
/// empty modules is skipped as an optimization.
Copy link
Contributor Author

@petrochenkov petrochenkov Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the optimization is removed, some diagnostics in UI tests still change, but due to other issues, like considering only a single immediate Module parent instead of walking the chain.

Block(Option<Module<'ra>>),

/// We passed through an impl or trait and are now in one of its
/// methods or associated types. Allow references to ty params that impl or trait
/// binds. Disallow any other upvars (including other ty params that are
Expand All @@ -210,7 +217,7 @@ pub(crate) enum RibKind<'ra> {
/// All other constants aren't allowed to use generic params at all.
ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>),

/// We passed through a module.
/// We passed through a module item.
Module(Module<'ra>),

/// We passed through a `macro_rules!` statement
Expand Down Expand Up @@ -242,6 +249,7 @@ impl RibKind<'_> {
pub(crate) fn contains_params(&self) -> bool {
match self {
RibKind::Normal
| RibKind::Block(..)
| RibKind::FnOrCoroutine
| RibKind::ConstantItem(..)
| RibKind::Module(_)
Expand All @@ -258,15 +266,8 @@ impl RibKind<'_> {
fn is_label_barrier(self) -> bool {
match self {
RibKind::Normal | RibKind::MacroDefinition(..) => false,

RibKind::AssocItem
| RibKind::FnOrCoroutine
| RibKind::Item(..)
| RibKind::ConstantItem(..)
| RibKind::Module(..)
| RibKind::ForwardGenericParamBan(_)
| RibKind::ConstParamTy
| RibKind::InlineAsmSym => true,
RibKind::FnOrCoroutine | RibKind::ConstantItem(..) => true,
kind => bug!("unexpected rib kind: {kind:?}"),
}
}
}
Expand Down Expand Up @@ -1527,19 +1528,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
ret
}

fn with_mod_rib<T>(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
let module = self.r.expect_module(self.r.local_def_id(id).to_def_id());
// Move down in the graph.
let orig_module = replace(&mut self.parent_scope.module, module);
self.with_rib(ValueNS, RibKind::Module(module), |this| {
this.with_rib(TypeNS, RibKind::Module(module), |this| {
let ret = f(this);
this.parent_scope.module = orig_module;
ret
})
})
}

fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) {
// For type parameter defaults, we have to ban access
// to following type parameters, as the GenericArgs can only
Expand Down Expand Up @@ -2677,20 +2665,25 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
}

ItemKind::Mod(..) => {
self.with_mod_rib(item.id, |this| {
if mod_inner_docs {
this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
}
let old_macro_rules = this.parent_scope.macro_rules;
visit::walk_item(this, item);
// Maintain macro_rules scopes in the same way as during early resolution
// for diagnostics and doc links.
if item.attrs.iter().all(|attr| {
!attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape)
}) {
this.parent_scope.macro_rules = old_macro_rules;
}
let module = self.r.expect_module(self.r.local_def_id(item.id).to_def_id());
let orig_module = replace(&mut self.parent_scope.module, module);
self.with_rib(ValueNS, RibKind::Module(module), |this| {
this.with_rib(TypeNS, RibKind::Module(module), |this| {
if mod_inner_docs {
this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
}
let old_macro_rules = this.parent_scope.macro_rules;
visit::walk_item(this, item);
// Maintain macro_rules scopes in the same way as during early resolution
// for diagnostics and doc links.
if item.attrs.iter().all(|attr| {
!attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape)
}) {
this.parent_scope.macro_rules = old_macro_rules;
}
})
});
self.parent_scope.module = orig_module;
}

ItemKind::Static(box ast::StaticItem {
Expand Down Expand Up @@ -2821,9 +2814,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// We also can't shadow bindings from associated parent items.
for ns in [ValueNS, TypeNS] {
for parent_rib in self.ribs[ns].iter().rev() {
// Break at mod level, to account for nested items which are
// Break at module or block level, to account for nested items which are
// allowed to shadow generic param names.
if matches!(parent_rib.kind, RibKind::Module(..)) {
if matches!(parent_rib.kind, RibKind::Module(..) | RibKind::Block(..)) {
break;
}

Expand Down Expand Up @@ -4652,16 +4645,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
debug!("(resolving block) entering block");
// Move down in the graph, if there's an anonymous module rooted here.
let orig_module = self.parent_scope.module;
let anonymous_module = self.r.block_map.get(&block.id).cloned(); // clones a reference
let anonymous_module = self.r.block_map.get(&block.id).copied();

let mut num_macro_definition_ribs = 0;
if let Some(anonymous_module) = anonymous_module {
debug!("(resolving block) found anonymous module, moving down");
self.ribs[ValueNS].push(Rib::new(RibKind::Module(anonymous_module)));
self.ribs[TypeNS].push(Rib::new(RibKind::Module(anonymous_module)));
self.ribs[ValueNS].push(Rib::new(RibKind::Block(Some(anonymous_module))));
self.ribs[TypeNS].push(Rib::new(RibKind::Block(Some(anonymous_module))));
self.parent_scope.module = anonymous_module;
} else {
self.ribs[ValueNS].push(Rib::new(RibKind::Normal));
self.ribs[ValueNS].push(Rib::new(RibKind::Block(None)));
}

// Descend into the block.
Expand Down
10 changes: 3 additions & 7 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -849,9 +849,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}

// Try to find in last block rib
if let Some(rib) = &self.last_block_rib
&& let RibKind::Normal = rib.kind
{
if let Some(rib) = &self.last_block_rib {
for (ident, &res) in &rib.bindings {
if let Res::Local(_) = res
&& path.len() == 1
Expand Down Expand Up @@ -900,7 +898,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
if path.len() == 1 {
for rib in self.ribs[ns].iter().rev() {
let item = path[0].ident;
if let RibKind::Module(module) = rib.kind
if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind
&& let Some(did) = find_doc_alias_name(self.r, module, item.name)
{
return Some((did, item));
Expand Down Expand Up @@ -2458,9 +2456,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}

if let RibKind::Module(module) = rib.kind
&& let ModuleKind::Block = module.kind
{
if let RibKind::Block(Some(module)) = rib.kind {
self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt));
} else if let RibKind::Module(module) = rib.kind {
// Encountered a module item, abandon ribs and look into that module and preludes.
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/resolve/issue-104700-inner_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,12 @@ fn main() {
if bar == 2 { //~ ERROR cannot find value
println!("yes");
}
{
let baz = 3;
struct S;
}
if baz == 3 { //~ ERROR cannot find value
println!("yes");
}
test_func(1); //~ ERROR cannot find function
}
16 changes: 14 additions & 2 deletions tests/ui/resolve/issue-104700-inner_scope.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@ help: the binding `bar` is available in a different scope in the same function
LL | let bar = 2;
| ^^^

error[E0425]: cannot find value `baz` in this scope
--> $DIR/issue-104700-inner_scope.rs:14:8
|
LL | if baz == 3 {
| ^^^
|
help: the binding `baz` is available in a different scope in the same function
--> $DIR/issue-104700-inner_scope.rs:11:13
|
LL | let baz = 3;
| ^^^

error[E0425]: cannot find function `test_func` in this scope
--> $DIR/issue-104700-inner_scope.rs:10:5
--> $DIR/issue-104700-inner_scope.rs:17:5
|
LL | test_func(1);
| ^^^^^^^^^ not found in this scope

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0425`.
Loading