Skip to content

fresh binding should shadow the def in expand #143141

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

bvanjoi
Copy link
Contributor

@bvanjoi bvanjoi commented Jun 28, 2025

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 28, 2025
@petrochenkov
Copy link
Contributor

I don't understand why this works and how it fixes the issue.
The LookAheadMacroDefinition are inserted for all macro definitions, regardless of whether it's macro or macro_rules, and nothing is done for use items.
The reversed rib walk in fn apply_pattern_bindings can also encounter arbitrary ribs before encountering a LookAheadMacroDefinition.

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 30, 2025
@petrochenkov
Copy link
Contributor

Also "shallow" -> "shadow" in the PR/commit messages and file names.

@bvanjoi
Copy link
Contributor Author

bvanjoi commented Jul 1, 2025

The reversed rib walk in fn apply_pattern_bindings can also encounter arbitrary ribs before encountering a LookAheadMacroDefinition.

There may be a bug if LookAheadMacroDefinition fails to apply correctly.

I don't understand why this works and how it fixes the issue.

LookAheadMacroDefinition stores the macro expansion result and serves as a fallback in resolve_ident_in_lexical_scope, ensuring it takes the correct resolution path:

// The ident resolves to a type parameter or local variable.
return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs(
i,
rib_ident,
*res,
finalize.map(|finalize| finalize.path_span),
*original_rib_ident_def,
ribs,
)));

@bvanjoi bvanjoi changed the title fresh binding should shallow the def after expand fresh binding should shadow the def in expand Jul 1, 2025
@bvanjoi
Copy link
Contributor Author

bvanjoi commented Jul 1, 2025

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 1, 2025
@petrochenkov
Copy link
Contributor

@bvanjoi
Are you sure this cannot successfully resolve some names that should not be resolved?
(I started reviewing yesterday, but it was late so I'll finish today or on Monday.)

@bvanjoi
Copy link
Contributor Author

bvanjoi commented Jul 12, 2025

Are you sure this cannot successfully resolve some names that should not be resolved?

I need to test this with additional cases across different rib contexts. @rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 12, 2025
@rustbot
Copy link
Collaborator

rustbot commented Jul 12, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@petrochenkov petrochenkov added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 26, 2025
@rust-log-analyzer

This comment has been minimized.

@bvanjoi bvanjoi marked this pull request as draft August 3, 2025 14:56
@rust-log-analyzer

This comment has been minimized.

@bvanjoi bvanjoi marked this pull request as ready for review August 5, 2025 17:06
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 5, 2025
@bvanjoi
Copy link
Contributor Author

bvanjoi commented Aug 5, 2025

Update: The key change in this patch introduces RibKind::Block, which consolidates both fresh bindings and macro definitions within a block scope, while verifying their accessibility at macro call sites.

@rustbot ready

// Descend into the block.
for stmt in &block.stmts {
if let StmtKind::Item(ref item) = stmt.kind
&& let ItemKind::MacroDef(..) = item.kind
{
num_macro_definition_ribs += 1;
let res = self.r.local_def_id(item.id).to_def_id();
self.ribs[ValueNS].push(Rib::new(RibKind::MacroDefinition(res)));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can remove RibKind::MacroDefinition in the future since all macro bindings can be accessed through RibKind::Block.

Copy link
Contributor

Choose a reason for hiding this comment

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

If the new scheme is already implemented, then it's better to remove it now to make sure everything is migrated correctly.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, it's still there due to labels, but labels are supposed to be resolved just like local variables, so they can migrate to the new scheme too.

@@ -4658,6 +4729,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// 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 is_module_block = anonymous_module.is_some();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can remove RibKind::Module in the future and use something like RibKind::Block { id: NodeId, module: Option<Module> }.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's do this, and only add Block ribs for blocks, not Module ribs or Normal ribs.
I'd rather do it before making changes from this PR.

);
}

fn collect_fresh_binding_in_pat(&mut self, pat: &ast::Pat) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you inline these 3 methods?
They are all small and are called only once.

@@ -4669,14 +4741,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.ribs[ValueNS].push(Rib::new(RibKind::Normal));
}

self.ribs[ValueNS]
.push(Rib::new(RibKind::Block { id: block.id, is_module: is_module_block }));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
.push(Rib::new(RibKind::Block { id: block.id, is_module: is_module_block }));
.push(Rib::new(RibKind::Block { id: block.id, is_module }));

Nit: rename for brevity.

// Descend into the block.
for stmt in &block.stmts {
if let StmtKind::Item(ref item) = stmt.kind
&& let ItemKind::MacroDef(..) = item.kind
{
num_macro_definition_ribs += 1;
let res = self.r.local_def_id(item.id).to_def_id();
self.ribs[ValueNS].push(Rib::new(RibKind::MacroDefinition(res)));
self.label_ribs.push(Rib::new(RibKind::MacroDefinition(res)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Labels have the same hygiene as local variables and are also resolved at macro definition site.

self.label_ribs.pop();
}
self.last_block_rib = self.ribs[ValueNS].pop();
let block_rib = self.ribs[ValueNS].pop(); // pop `RibKind::Block`
if !is_module_block {
Copy link
Contributor

Choose a reason for hiding this comment

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

Modules are indeed not created for some blocks, but that's only a performance optimization.
There should be zero semantic difference between module blocks and non-module blocks, but this PR seems to add some.

Copy link
Contributor

Choose a reason for hiding this comment

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

If I remove the optimization and run ui tests some diagnostics change.
That's not very good, it means there are some pre-existing issues with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but this PR seems to add some

They're only to generate last_block_rib which is only used for diagnostics, specifically to display information about non-module blocks:

// Try to find in last block rib
if let Some(rib) = &self.last_block_rib
&& let RibKind::Normal = rib.kind
{
for (ident, &res) in &rib.bindings {
if let Res::Local(_) = res
&& path.len() == 1
&& ident.span.eq_ctxt(path[0].ident.span)
&& ident.name == path[0].ident.name
{
err.span_help(
ident.span,
format!("the binding `{path_str}` is available in a different scope in the same function"),
);
return (true, suggested_candidates, candidates);
}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

specifically to display information about non-module blocks

That's exactly what I'm talking about - a pre-existing bug.
This code should work for all blocks, regardless of whether those blocks potentially have items in them or not.

@petrochenkov
Copy link
Contributor

(I didn't review everything yet, will continue later this week.)

}
LookaheadItemInBlock::MacroDef { bindings: macro_bindings, .. } => {
let bindings =
bindings.last().unwrap().1.iter().filter_map(|(name, res)| {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
bindings.last().unwrap().1.iter().filter_map(|(name, res)| {
bindings.last().unwrap().1.iter().copied().filter(|(name, _)| !need_removed.contains(name))


if let Some(last_pat_id) = last_pat_id
&& let RibKind::Block { id: block, .. } = self.ribs[ValueNS].last_mut().unwrap().kind
&& let Some(items) = self.r.lookahead_items_in_block.get_mut(&block)
Copy link
Contributor

Choose a reason for hiding this comment

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

So, in

let a = 0;
{
  let b = 1;
  macro_rules! m { ... }
}

only b will be added to LookaheadItemInBlock::MacroDef bindings for m, but not a.

Is this ok?
If yes, why is this ok?

let a0: BindingF = m!(); //~ NOTE in this expansion of m!
let f = || -> BindingF { 42 };
let a1: BindingF = m!();
macro m() { f() } //~ ERROR cannot find function `f` in this scope
Copy link
Contributor

Choose a reason for hiding this comment

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

f can be found at m's definition site, we should report a different error in this case, like "f is not yet alive at point of use" or something.

@petrochenkov
Copy link
Contributor

Could you also add a test case demonstrating what happens with this example?

fn f() {
    let a = 10;
    
    #[macro_export]
    macro_rules! m { () => { a } }
}

fn main() {
    fn a() {}

    crate::m!();
}

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 7, 2025
@bors
Copy link
Collaborator

bors commented Aug 8, 2025

☔ The latest upstream changes (presumably #145077) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Hygiene of used macro item is weird.
5 participants