From 3d7f6c567eeda3fea0a2d9e6b1f52c49f71edbc8 Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Mon, 13 Oct 2025 13:57:28 -0700 Subject: [PATCH 1/2] Expand name resolution stub --- src/items/use-declarations.md | 56 +---- src/macros-by-example.md | 41 ++++ src/names/name-resolution.md | 371 ++++++++++++++++++++++++++++++++++ src/names/namespaces.md | 1 + 4 files changed, 417 insertions(+), 52 deletions(-) diff --git a/src/items/use-declarations.md b/src/items/use-declarations.md index 94638b6a12..284f2be3ca 100644 --- a/src/items/use-declarations.md +++ b/src/items/use-declarations.md @@ -116,6 +116,7 @@ They may create bindings for: * [Built-in types] * [Attributes] * [Derive macros] +* [Macros by example] r[items.use.path.disallowed] They cannot import [associated items], [generic parameters], [local variables], paths with [`Self`], or [tool attributes]. More restrictions are described below. @@ -389,58 +390,9 @@ r[items.use.restrictions.variant] use TypeAlias::MyVariant; //~ ERROR ``` -r[items.use.ambiguities] -## Ambiguities - -> [!NOTE] -> This section is incomplete. - -r[items.use.ambiguities.intro] -Some situations are an error when there is an ambiguity as to which name a `use` declaration refers. This happens when there are two name candidates that do not resolve to the same entity. - -r[items.use.ambiguities.glob] -Glob imports are allowed to import conflicting names in the same namespace as long as the name is not used. -For example: - -```rust -mod foo { - pub struct Qux; -} - -mod bar { - pub struct Qux; -} - -use foo::*; -use bar::*; //~ OK, no name conflict. - -fn main() { - // This would be an error, due to the ambiguity. - //let x = Qux; -} -``` - -Multiple glob imports are allowed to import the same name, and that name is allowed to be used, if the imports are of the same item (following re-exports). The visibility of the name is the maximum visibility of the imports. For example: - -```rust -mod foo { - pub struct Qux; -} - -mod bar { - pub use super::foo::Qux; -} - -// These both import the same `Qux`. The visibility of `Qux` -// is `pub` because that is the maximum visibility between -// these two `use` declarations. -pub use bar::*; -use foo::*; - -fn main() { - let _: Qux = Qux; -} -``` +TODO mention ambiguities and link to name-res. Moved to name-res because +ambiguities are fundamentally a product of the place of use, not the use +declaration. [`extern crate`]: extern-crates.md [`macro_rules`]: ../macros-by-example.md diff --git a/src/macros-by-example.md b/src/macros-by-example.md index 2fa104ed4d..f582fee93c 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -326,6 +326,28 @@ fn foo() { // m!(); // Error: m is not in scope. ``` +* textual scope name bindings for macros may shadow path-based scope bindings + to macros + +```rust +macro_rules! m { + () => { + println!("m"); + }; +} + +#[macro_export] +macro_rules! m2 { + () => { + println!("m2"); + }; +} + +use crate::m2 as m; + +m!(); // prints "m\n" +``` + r[macro.decl.scope.macro_use] ### The `macro_use` attribute @@ -480,6 +502,25 @@ By default, macros only have [textual scope][macro.decl.scope.textual] and canno > # fn main() {} > ``` +r[macro.decl.scope.path.reexport] + +* macros can be re-exported to give them path-based scope from a module other than the crate root. + * there's some visibility stuff here that may already be mentioned + elsewhere. I'm pretty sure that w/o a #[macro_export] the macro being + re-exported is implicitly pub(crate) and with one it is implicitly pub. + The later is mentioned below, don't remember where I saw the former. + +``` +mac::m!(); // OK: Path-based lookup finds m in the mac module. + +mod mac { + macro_rules! m { + () => {}; + } + pub(crate) use m; +} +``` + r[macro.decl.scope.macro_export.export] The `macro_export` attribute causes a macro to be exported from the crate root so that it can be referred to in other crates by path. diff --git a/src/names/name-resolution.md b/src/names/name-resolution.md index da60d7eb32..b18458e82f 100644 --- a/src/names/name-resolution.md +++ b/src/names/name-resolution.md @@ -1,4 +1,375 @@ +r[names.resolution] # Name resolution +r[names.resolution.intro] + +_Name resolution_ is the process of tying paths and other identifiers to the declarations of those entities. Names are segregated into different [namespaces], allowing entities in different namespaces to share the same name without conflict. Each name is valid within a [scope], or a region of source text where that name may be referenced. Access to certain names may be restricted based on their [visibility]. + +* Names are resolved at three different stages of compilation. +* [Macros] and [use declarations] are resolved during macro expansion. + * This stage of resolution is known as "Early Resolution". +* `Type::assoc_item`, `::assoc_item`, `::Variant` and `EnumTyAlias::Variant` are resolved during type checking + * `Trait::assoc_item`, `::assoc_item` and `Enum::Variant` are resolved during late resolution + * This stage of resolution is known as type-relative resolution. + * in reality this is never talked about so I doubt it has a name yet. +* All other names are resolved during AST lowering. + * This stage of resolution is known as "Late Resolution". + * Note, late resolution occurs before type dependent resolution. + +r[names.resolution.early] +## Early name resolution + +r[names.resolution.early.intro] + +* early name resolution is the part of name resolution that happens during macro expansion +* early name resolution includes the resolution of imports and macros +* early name resolution is the minimum amount of resolution required to resolve macro invocations so they can be expanded. +* resolving imports is necessary to resolve macro invocations + * specifically for path-based scope macro resolutions, this can occur + either because of a `#[macro_export]`, a proc macro definition, or a + reexport of a macro in 2018 or later code. +* resolving macro invocations and tying them to macro declarations is necessary so they can be expanded +* this process is iterative and repeats until there are no remaining unexpanded macro invocations (fixed point algorithm) +* Post expansion these resolutions are checked again to ensure no new ambiguities were introduced by the expansion process + * This causes so called time traveling ambiguities, such as when a glob import introduces an item that is ambiguous with its own base path. + +TODO Document some time traveling ambiguitie examples, place in relevant ambiguity section + +r[names.resolution.early.imports] + +* All imports are fully resolved at this point. + * imports of names that cannot be fully resolved during macro expansion, such as those depending on type information, are not supported and will produce an error. + +TODO example of unsupported type dependent import + +r[names.resolution.early.imports.shadowing] + +The following is a list of situations where shadowing of use declarations is permitted: + +* [use glob shadowing] +* [macro textual scope shadowing] + +r[names.resolution.early.imports.errors] +r[names.resolution.early.imports.errors.ambiguity] + +* shadowing and ambiguity may or may not represent the same section or one may be a subsection of the other + +* Builtin Attributes +* Derive Helpers +* Textual Vs Path-based Scope +* Glob vs Outer +* Glob vs Glob +* ~~Glob vs Expanded~~ pretty certain we don't want to mention this one +* More Expanded vs Outer + +r[names.resolution.expansion.imports.errors.ambiguity] +## Ambiguities + +> [!NOTE] +> This section is incomplete. + +r[items.use.ambiguities.intro] +Some situations are an error when there is an ambiguity as to which name a `use` declaration refers. This happens when there are two name candidates that do not resolve to the same entity. + +* except where shadowing is allowed +r[names.resolution.early.imports.errors.ambiguity.globvsglob] +* it is an error to name an item through ambiguous use declarations + * two globs imports which both have an item matching that name where the items are different + * this is not an error even if is a third non glob binding resolution to an item with the same name +* it is not an error to have two glob imports which include items which would be ambiguous so long as you do not name one of those items through the ambiguous glob imports + * Should this live alongside use decls item page or in the name resolution page? + +r[items.use.ambiguities.glob] +Glob imports are allowed to import conflicting names in the same namespace as long as the name is not used. +For example: + +TODO: move this section? It's documenting a situation that _isnt_ an ambiguity +error. I've been working off of a pattern I think I saw in a few other +locations, where we have specific error sections that document all of the +reference relevant error cases associated with an some part of the language. + * This section does technically document globvsglob ambituity errors, but + it does so indirectly. We never explicitly mention "you can't resolve a + name through a glob import when there are multiple candidate glob imports + in scope that each resolve to different entities". We just say "you can + do that if you don't actually use the ambiguious names" and have an + example that shows that trying to use the name would be an error. + +```rust +mod foo { + pub struct Qux; +} + +mod bar { + pub struct Qux; +} + +use foo::*; +use bar::*; //~ OK, no name conflict. + +fn ambiguous_use() { + // This would be an error, due to the ambiguity. + //let x = Qux; +} + +fn ambiguous_shadow() { + // This is permitted, since resolution is not through the ambiguous globs + struct Qux; + let x = Qux; +} +``` + + +Multiple glob imports are allowed to import the same name, and that name is allowed to be used, if the imports are of the same item (following re-exports). The visibility of the name is the maximum visibility of the imports. For example: + +```rust +mod foo { + pub struct Qux; +} + +mod bar { + pub use super::foo::Qux; +} + +// These both import the same `Qux`. The visibility of `Qux` +// is `pub` because that is the maximum visibility between +// these two `use` declarations. +pub use bar::*; +use foo::*; + +fn main() { + let _: Qux = Qux; +} +``` + +r[names.resolution.early.imports.errors.ambiguity.builtin-attr] +* it is an error to use a user defined attribute or derive macro with the same name as a builtin attribute (e.g. inline) + * I think we may special case this one and allow certain kinds of + ambiguities where the builtin-attr is shadowed by a user attribute (not + sure if this actually exists or is just proposed, TODO investigate) + + +```rust,ignore +// myinline/src/lib.rs +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn inline(_attr: TokenStream, item: TokenStream) -> TokenStream { + item +} +``` + + +```rust,ignore +// src/lib.rs +use myinline::inline; +use myinline::inline as myinline; + +#[myinline::inline] +pub fn foo() {} + +#[crate::inline] +pub fn bar() {} + +#[myinline] +pub fn baz() {} + +#[inline] // ERROR `inline` is ambiguous +pub fn quix() {} +``` + +r[names.resolution.early.imports.errors.ambiguity.derivehelper] +* derive helpers used before their associated derive may not shadow other attributes or other derive helpers that are otherwise in scope after their derive + * TODO example? This ones harder to do concisely afaik + +Helper attributes may not be used before the macro that introduces them. + +* What happens if two macros introduce the same helper, will the second one not + be able to see the attribute of the first anymore, assuming their order is + "firstmacro" "helper" "secondmacro"? + +> [!NOTE] +> rustc currently allows derive helpers to be used before their attribute macro +> introduces them into scope so long as they do not shadow any other attributes +> or derive helpers that are otherwise correctly in scope. This behavior +> deprecated and slated for removal. +> +> ```rust,ignore +> #[helper] // deprecated, hard error in the future +> #[derive(WithHelperAttr)] +> struct Struct { +> field: (), +> } +> ``` +> +> For more details, see [Rust issue #79202](https://github.com/rust-lang/rust/issues/79202). + + +r[names.resolution.early.imports.errors.ambiguity.textualvspathbasedscope] +* path-based scope bindings for macros may not shadow textual scope bindings to macros + * This is sort of an intersection between macros and imports, because at + least in stable rust you can only get path-based macro resolutions from + imports of mbe macros (and presumably from proc macro crates), but you + can only get textual scope of macros from macro declarations + * https://doc.rust-lang.org/nightly/reference/names/namespaces.html#r-names.namespaces.sub-namespaces.use-shadow + * [macro.decl.scope.path.ambiguity] +r[names.resolution.early.imports.errors.ambiguity.globvsouter] +* it is an error to shadow an outer name binding with a glob import + * This seems to only apply to early resolution (duh, I documented this as part of an early resolution codepath) + * // Below we report various ambiguity errors. + // We do not need to report them if we are either in speculative resolution, + // or in late resolution when everything is already imported and expanded + // and no ambiguities exist. + * I attempted to produce an example using structs and it allowed the outer import to shadow the inner glob just fine + +```rust +mod bar { + pub struct Name; +} + +mod baz { + pub struct Name; +} + +use baz::Name; + +pub fn foo() { + use bar::*; + Name; +} +``` + +* I'd like to have a better understanding of why this doesn't trigger ambiguity errors. + * I'm taking a guess but I think it has to do with how and when we resolve + names during early resolution. We resolve all the imports but ambiguities + only occur when observed, so we'd need to try to resolve Name during + early resolution which simply won't happen because it is a struct so it + will never be visited for resolution during expansion. + * We will end up resolving the imports themselves, but they'll resolve fine + because the imports themselves aren't ambiguous + * By the time we get to late resolution we no longer expect there to be any + ambiguities, so we will happily return the first resolution result and + never search for additional ambiguities, so we resolve directly to + `bar::Name` through the glob import + + * doing it with macros produced the expected error +```rust +mod bar { + macro_rules! name { + () => {} + } + pub(crate) use name; +} + +mod baz { + macro_rules! name { + () => {} + } + pub(crate) use name; +} + +use baz::name; + +pub fn foo() { + use bar::*; + name!(); // ERROR `name` is ambiguous +} +``` + +* how does it work with imports? The same as macros, same error during early resolution + +```rust +mod bar { + pub mod foo { + pub struct Name; + } +} + +mod baz { + pub mod foo { + pub struct Name; + } +} + +use baz::foo; + +pub fn foo() { + use bar::*; + use foo::Name; // `foo` is ambiguous +} +``` + +r[macro.decl.scope.textual.ambiguity.moreexpandedvsouter] +* it is an error for name bindings from macro expansions to shadow name bindings from outside of those expansions + +```rust +macro_rules! name { + () => {} +} + +macro_rules! define_name { + () => { + macro_rules! name { + () => {} + } + } +} + +fn foo() { + define_name!(); + name!(); // ERROR `name` is ambiguous +} +``` + + +r[names.resolution.early.imports.errors.ambiguity.globvsexpanded] +* Grey Area + +r[names.resolution.early.macros] + +* .visitation-order + * derive helpers + * not visited when resolving derive macros in the parent scope (starting scope) + * derive helpers compat + * always visited + * macro rules bindings (textual scope macros) + * always visited + * modules (path-based scope macros) + * always visited + * macrouseprelude + * not visited in 2018 and later when `#[no_implicit_prelude]` is present + * stdlibprelude + * always visited for macro resolutions + * is it? what about no-std + no-core? + * builtinattrs + * always visited +* .subnamespaces + * macros are split into two subnamespaces, one for bang macros, and the other for attributes and derives. Resolution candidates from the incorrect subnamespace are ignored + * https://doc.rust-lang.org/nightly/reference/names/namespaces.html#r-names.namespaces.sub-namespaces + +r[names.resolution.early.macros.errors.reserved-names] + +the names cfg and cfg_attr are reserved in the macro attribute sub-namespace + +* https://doc.rust-lang.org/nightly/reference/names/namespaces.html#r-names.namespaces.sub-namespaces + +r[names.resolution.late] + > [!NOTE] +> > This is a placeholder for future expansion. + +r[names.resolution.type-dependent] + +> [!NOTE] +> +> This is a placeholder for future expansion. + +[use glob shadowing]: ../items/use-declarations.md#r-items.use.glob.shadowing +[Macros]: ../macros.md +[use declarations]: ../items/use-declarations.md +[macro textual scope shadowing]: ../macros-by-example.md#r-macro.decl.scope.textual.shadow +[`let` bindings]: ../statements.md#let-statements +[item definitions]: ../items.md +[namespaces]: ../names/namespaces.md +[scope]: ../names/scopes.md +[visibility]: ../visibility-and-privacy.md diff --git a/src/names/namespaces.md b/src/names/namespaces.md index b3560d2c19..8050cfd1f7 100644 --- a/src/names/namespaces.md +++ b/src/names/namespaces.md @@ -119,6 +119,7 @@ For example, the [`cfg` attribute] and the [`cfg` macro] are two different entit r[names.namespaces.sub-namespaces.use-shadow] It is still an error for a [`use` import] to shadow another macro, regardless of their sub-namespaces. +* TODO revisit [`cfg` attribute]: ../conditional-compilation.md#the-cfg-attribute [`cfg` macro]: ../conditional-compilation.md#the-cfg-macro From 64c29bd9a4bc0e66edbb1941bd1213902c5eb3b2 Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Wed, 29 Oct 2025 12:08:34 -0700 Subject: [PATCH 2/2] rewrite bullet points as prose --- src/items/use-declarations.md | 11 +- src/macros-by-example.md | 84 ++++--- src/macros.md | 10 + src/names/name-resolution.md | 420 ++++++++++++++++------------------ src/names/namespaces.md | 7 +- src/procedural-macros.md | 5 + 6 files changed, 281 insertions(+), 256 deletions(-) diff --git a/src/items/use-declarations.md b/src/items/use-declarations.md index 284f2be3ca..120b7a96b5 100644 --- a/src/items/use-declarations.md +++ b/src/items/use-declarations.md @@ -116,7 +116,7 @@ They may create bindings for: * [Built-in types] * [Attributes] * [Derive macros] -* [Macros by example] +* [`macro_rules`] r[items.use.path.disallowed] They cannot import [associated items], [generic parameters], [local variables], paths with [`Self`], or [tool attributes]. More restrictions are described below. @@ -303,6 +303,10 @@ mod clashing { } ``` +> [!NOTE] +> +> For areas where shadowing is not allowed, see [name resolution ambiguities]. + r[items.use.glob.last-segment-only] `*` cannot be used as the first or intermediate segments. @@ -390,10 +394,6 @@ r[items.use.restrictions.variant] use TypeAlias::MyVariant; //~ ERROR ``` -TODO mention ambiguities and link to name-res. Moved to name-res because -ambiguities are fundamentally a product of the place of use, not the use -declaration. - [`extern crate`]: extern-crates.md [`macro_rules`]: ../macros-by-example.md [`self`]: ../paths.md#self @@ -412,3 +412,4 @@ declaration. [tool attributes]: ../attributes.md#tool-attributes [type alias]: type-aliases.md [type namespace]: ../names/namespaces.md +[name resolution ambiguities]: ../names/name-resolution.html#r-names.resolution.expansion.imports.errors.ambiguity diff --git a/src/macros-by-example.md b/src/macros-by-example.md index f582fee93c..b94a5f9eeb 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -326,20 +326,22 @@ fn foo() { // m!(); // Error: m is not in scope. ``` -* textual scope name bindings for macros may shadow path-based scope bindings - to macros +r[macro.decl.scope.textual.shadow.path-based] +Textual scope name bindings for macros may shadow path-based scope bindings to macros. ```rust -macro_rules! m { +#[macro_export] +macro_rules! m2 { () => { - println!("m"); + println!("m2"); }; } -#[macro_export] -macro_rules! m2 { +m!(); // prints "m2\n" + +macro_rules! m { () => { - println!("m2"); + println!("m"); }; } @@ -348,6 +350,53 @@ use crate::m2 as m; m!(); // prints "m\n" ``` +> [!NOTE] +> +> For areas where shadowing is not allowed, see [name resolution ambiguities]. + +r[macro.decl.scope.path-based] +### Path-based scope + +r[macro.decl.scope.path-based.intro] +By default, a macro has no path-based scope. Macros can gain path-based scope in two ways: + +* [Use declaration re-export] +* [`#[macro_export]`](macros-by-example.html#the-macro_export-attribute) + +r[macro.decl.scope.path.reexport] +Macros can be re-exported to give them path-based scope from a module other than the crate root. + +```rust +mac::m!(); // OK: Path-based lookup finds m in the mac module. + +mod mac { + macro_rules! m { + () => {}; + } + pub(crate) use m; +} +``` + +r[macro.decl.scope.path-based.visibility] +Macros have an implicit visibility of `pub(crate)`. `#[macro_export]` changes the implicit visibility to `pub`. + +```rust,compile_fail,E0364 +macro_rules! private_m { + () => {}; +} + +#[macro_export] +macro_rules! pub_m { + () => {}; +} + +pub(crate) use private_m as private_macro; // OK +pub use pub_m as pub_macro; // OK + +pub use private_m; // ERROR: `private_m` is only public within + // the crate and cannot be re-exported outside +``` + r[macro.decl.scope.macro_use] ### The `macro_use` attribute @@ -502,25 +551,6 @@ By default, macros only have [textual scope][macro.decl.scope.textual] and canno > # fn main() {} > ``` -r[macro.decl.scope.path.reexport] - -* macros can be re-exported to give them path-based scope from a module other than the crate root. - * there's some visibility stuff here that may already be mentioned - elsewhere. I'm pretty sure that w/o a #[macro_export] the macro being - re-exported is implicitly pub(crate) and with one it is implicitly pub. - The later is mentioned below, don't remember where I saw the former. - -``` -mac::m!(); // OK: Path-based lookup finds m in the mac module. - -mod mac { - macro_rules! m { - () => {}; - } - pub(crate) use m; -} -``` - r[macro.decl.scope.macro_export.export] The `macro_export` attribute causes a macro to be exported from the crate root so that it can be referred to in other crates by path. @@ -765,3 +795,5 @@ For more detail, see the [formal specification]. [Repetitions]: #repetitions [token]: tokens.md [`$crate`]: macro.decl.hygiene.crate +[Use declaration re-export]: items/use-declarations.html#use-visibility +[name resolution ambiguities]: ../names/name-resolution.html#r-names.resolution.expansion.imports.errors.ambiguity diff --git a/src/macros.md b/src/macros.md index 36287e0d4a..4ab14fd898 100644 --- a/src/macros.md +++ b/src/macros.md @@ -106,6 +106,16 @@ macro_rules! example { example!(); ``` +r[macro.invocation.name-resolution] + +Macros invocations can be resolved via two kinds of scopes: + +* Textual Scope + * [textual scope `macro_rules`](macros-by-example.md#r-macro.decl.scope.textual) +* Path-based scope + * [path-based scope `macro_rules`](macros-by-example.md#r-macro.decl.scope.path-based) + * [Procedural Macros] + [Macros by Example]: macros-by-example.md [Procedural Macros]: procedural-macros.md [associated items]: items/associated-items.md diff --git a/src/names/name-resolution.md b/src/names/name-resolution.md index b18458e82f..05899500e5 100644 --- a/src/names/name-resolution.md +++ b/src/names/name-resolution.md @@ -5,94 +5,96 @@ r[names.resolution.intro] _Name resolution_ is the process of tying paths and other identifiers to the declarations of those entities. Names are segregated into different [namespaces], allowing entities in different namespaces to share the same name without conflict. Each name is valid within a [scope], or a region of source text where that name may be referenced. Access to certain names may be restricted based on their [visibility]. -* Names are resolved at three different stages of compilation. -* [Macros] and [use declarations] are resolved during macro expansion. - * This stage of resolution is known as "Early Resolution". -* `Type::assoc_item`, `::assoc_item`, `::Variant` and `EnumTyAlias::Variant` are resolved during type checking - * `Trait::assoc_item`, `::assoc_item` and `Enum::Variant` are resolved during late resolution - * This stage of resolution is known as type-relative resolution. - * in reality this is never talked about so I doubt it has a name yet. -* All other names are resolved during AST lowering. - * This stage of resolution is known as "Late Resolution". - * Note, late resolution occurs before type dependent resolution. +Name resolution is split into three stages throughout the compilation process. The first stage, Expansion-time resolution, resolves all [use declarations] and [macro invocations]. The second stage, Primary resolution, resolves all names that have not yet been resolved that do not depend on type information to resolve. The last stage, Type-relative resolution, resolves the remaining names once type information is available. -r[names.resolution.early] -## Early name resolution +> Note +> +> * Expansion-time resolution is also known as "Early Resolution" +> * Primary resolution is also known as "Late Resolution" -r[names.resolution.early.intro] +r[names.resolution.expansion] +## Expansion-time name resolution -* early name resolution is the part of name resolution that happens during macro expansion -* early name resolution includes the resolution of imports and macros -* early name resolution is the minimum amount of resolution required to resolve macro invocations so they can be expanded. -* resolving imports is necessary to resolve macro invocations - * specifically for path-based scope macro resolutions, this can occur - either because of a `#[macro_export]`, a proc macro definition, or a - reexport of a macro in 2018 or later code. -* resolving macro invocations and tying them to macro declarations is necessary so they can be expanded -* this process is iterative and repeats until there are no remaining unexpanded macro invocations (fixed point algorithm) -* Post expansion these resolutions are checked again to ensure no new ambiguities were introduced by the expansion process - * This causes so called time traveling ambiguities, such as when a glob import introduces an item that is ambiguous with its own base path. +r[names.resolution.expansion.intro] -TODO Document some time traveling ambiguitie examples, place in relevant ambiguity section +Expansion-time name resolution is the stage of name resolution necessary to complete macro expansion and fully generate a crate's AST. This stage requires the resolution of macro invocations and use declarations. Resolving use declarations is required to resolve [path-based scope] macro invocations. Resolving macro invocations is required in order to expand them. -r[names.resolution.early.imports] +The expansion process is iterative, alternately resolving imports, resolving and expanding macro invocations, then repeating until there are no further macros invocations to resolve. Once this process is completed all the imports are resolved again to ensure that the macro expansion process did not introduce any new ambiguious imports. -* All imports are fully resolved at this point. - * imports of names that cannot be fully resolved during macro expansion, such as those depending on type information, are not supported and will produce an error. +> Note +> +> This causes so called time traveling ambiguities, such as when a glob import introduces an item that is ambiguous with its own base path. +> +> ```rust,compile_fail,E0659 +> macro_rules! m { +> () => { mod bar {} } +> } +> +> mod bar { +> pub(crate) use m; +> } +> +> fn f() { +> // * initially speculatively resolve bar to the module in the crate root +> // * expansion of m introduces a second bar module inside the body of f +> // * expansion-time resolution finalizes resolutions by re-resolving all +> // imports and macro invocations, sees the introduced ambiguity +> // and reports it as an error +> bar::m!(); // ERROR `bar` is ambiguous +> } +> ``` -TODO example of unsupported type dependent import +r[names.resolution.expansion.imports] -r[names.resolution.early.imports.shadowing] +All use declarations are fully resolved during this stage of resolution. Type-relative paths cannot be resolved at this stage of compilation and will produce an error. -The following is a list of situations where shadowing of use declarations is permitted: +```rust,compile_fail,E0432 +mod my_mod { + pub const Const: () = (); -* [use glob shadowing] -* [macro textual scope shadowing] + pub enum MyEnum { + MyVariant + } -r[names.resolution.early.imports.errors] -r[names.resolution.early.imports.errors.ambiguity] + impl MyEnum { + pub const Const: () = (); + } -* shadowing and ambiguity may or may not represent the same section or one may be a subsection of the other + pub type TypeAlias = MyEnum; +} -* Builtin Attributes -* Derive Helpers -* Textual Vs Path-based Scope -* Glob vs Outer -* Glob vs Glob -* ~~Glob vs Expanded~~ pretty certain we don't want to mention this one -* More Expanded vs Outer +fn foo() { + // imports resolved at expansion-time + use my_mod::MyEnum; // OK + use my_mod::MyEnum::MyVariant; // OK + use my_mod::TypeAlias; // OK + use my_mod::TypeAlias::MyVariant; // Doesn't work + use my_mod::MyEnum::Const; // Doesn't work + use my_mod::Const; // OK + + // expressions resolved during type-relative resolution + let _ = my_mod::TypeAlias::MyVariant; // OK + let _ = my_mod::MyEnum::Const; // OK +} +``` -r[names.resolution.expansion.imports.errors.ambiguity] -## Ambiguities +r[names.resolution.expansion.imports.shadowing] -> [!NOTE] -> This section is incomplete. +The following is a list of situations where shadowing of use declarations is permitted: -r[items.use.ambiguities.intro] -Some situations are an error when there is an ambiguity as to which name a `use` declaration refers. This happens when there are two name candidates that do not resolve to the same entity. +* [use glob shadowing] +* [macro textual scope shadowing] -* except where shadowing is allowed -r[names.resolution.early.imports.errors.ambiguity.globvsglob] -* it is an error to name an item through ambiguous use declarations - * two globs imports which both have an item matching that name where the items are different - * this is not an error even if is a third non glob binding resolution to an item with the same name -* it is not an error to have two glob imports which include items which would be ambiguous so long as you do not name one of those items through the ambiguous glob imports - * Should this live alongside use decls item page or in the name resolution page? +r[names.resolution.expansion.imports.ambiguity] +## Ambiguities -r[items.use.ambiguities.glob] -Glob imports are allowed to import conflicting names in the same namespace as long as the name is not used. -For example: +r[names.resolution.expansion.imports.ambiguity.intro] +Some situations are an error when there is an ambiguity as to which name a `use` declaration refers. This happens when there are two name candidates that do not resolve to the same entity where neither import is [permitted] to shadow the other. + +r[names.resolution.expansion.imports.ambiguity.globvsglob] +Names may not be resolved through ambiguous glob statements. Glob imports are allowed to import conflicting names in the same namespace as long as the name is not used. Conflicting names from ambiguous glob statements may still be shadowed and used without producing an error. The errors occur at time of use, not time of import. -TODO: move this section? It's documenting a situation that _isnt_ an ambiguity -error. I've been working off of a pattern I think I saw in a few other -locations, where we have specific error sections that document all of the -reference relevant error cases associated with an some part of the language. - * This section does technically document globvsglob ambituity errors, but - it does so indirectly. We never explicitly mention "you can't resolve a - name through a glob import when there are multiple candidate glob imports - in scope that each resolve to different entities". We just say "you can - do that if you don't actually use the ambiguious names" and have an - example that shows that trying to use the name would be an error. +For example: ```rust mod foo { @@ -118,7 +120,6 @@ fn ambiguous_shadow() { } ``` - Multiple glob imports are allowed to import the same name, and that name is allowed to be used, if the imports are of the same item (following re-exports). The visibility of the name is the maximum visibility of the imports. For example: ```rust @@ -141,118 +142,31 @@ fn main() { } ``` -r[names.resolution.early.imports.errors.ambiguity.builtin-attr] -* it is an error to use a user defined attribute or derive macro with the same name as a builtin attribute (e.g. inline) - * I think we may special case this one and allow certain kinds of - ambiguities where the builtin-attr is shadowed by a user attribute (not - sure if this actually exists or is just proposed, TODO investigate) - - -```rust,ignore -// myinline/src/lib.rs -use proc_macro::TokenStream; - -#[proc_macro_attribute] -pub fn inline(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} -``` - - -```rust,ignore -// src/lib.rs -use myinline::inline; -use myinline::inline as myinline; +r[names.resolution.early.imports.ambiguity.globvsouter] +it is an error to shadow an outer name binding with a glob import. -#[myinline::inline] -pub fn foo() {} - -#[crate::inline] -pub fn bar() {} - -#[myinline] -pub fn baz() {} - -#[inline] // ERROR `inline` is ambiguous -pub fn quix() {} -``` - -r[names.resolution.early.imports.errors.ambiguity.derivehelper] -* derive helpers used before their associated derive may not shadow other attributes or other derive helpers that are otherwise in scope after their derive - * TODO example? This ones harder to do concisely afaik - -Helper attributes may not be used before the macro that introduces them. - -* What happens if two macros introduce the same helper, will the second one not - be able to see the attribute of the first anymore, assuming their order is - "firstmacro" "helper" "secondmacro"? - -> [!NOTE] -> rustc currently allows derive helpers to be used before their attribute macro -> introduces them into scope so long as they do not shadow any other attributes -> or derive helpers that are otherwise correctly in scope. This behavior -> deprecated and slated for removal. -> -> ```rust,ignore -> #[helper] // deprecated, hard error in the future -> #[derive(WithHelperAttr)] -> struct Struct { -> field: (), -> } -> ``` -> -> For more details, see [Rust issue #79202](https://github.com/rust-lang/rust/issues/79202). - - -r[names.resolution.early.imports.errors.ambiguity.textualvspathbasedscope] -* path-based scope bindings for macros may not shadow textual scope bindings to macros - * This is sort of an intersection between macros and imports, because at - least in stable rust you can only get path-based macro resolutions from - imports of mbe macros (and presumably from proc macro crates), but you - can only get textual scope of macros from macro declarations - * https://doc.rust-lang.org/nightly/reference/names/namespaces.html#r-names.namespaces.sub-namespaces.use-shadow - * [macro.decl.scope.path.ambiguity] -r[names.resolution.early.imports.errors.ambiguity.globvsouter] -* it is an error to shadow an outer name binding with a glob import - * This seems to only apply to early resolution (duh, I documented this as part of an early resolution codepath) - * // Below we report various ambiguity errors. - // We do not need to report them if we are either in speculative resolution, - // or in late resolution when everything is already imported and expanded - // and no ambiguities exist. - * I attempted to produce an example using structs and it allowed the outer import to shadow the inner glob just fine - -```rust +```rust,compile_fail,E0659 mod bar { - pub struct Name; + pub mod foo { + pub struct Name; + } } mod baz { - pub struct Name; + pub mod foo { + pub struct Name; + } } -use baz::Name; +use baz::foo; -pub fn foo() { +pub fn qux() { use bar::*; - Name; + use foo::Name; // `foo` is ambiguous } ``` -* I'd like to have a better understanding of why this doesn't trigger ambiguity errors. - * I'm taking a guess but I think it has to do with how and when we resolve - names during early resolution. We resolve all the imports but ambiguities - only occur when observed, so we'd need to try to resolve Name during - early resolution which simply won't happen because it is a struct so it - will never be visited for resolution during expansion. - * We will end up resolving the imports themselves, but they'll resolve fine - because the imports themselves aren't ambiguous - * By the time we get to late resolution we no longer expect there to be any - ambiguities, so we will happily return the first resolution result and - never search for additional ambiguities, so we resolve directly to - `bar::Name` through the glob import - - * doing it with macros produced the expected error -```rust +```rust,compile_fail,E0659 mod bar { macro_rules! name { () => {} @@ -275,33 +189,34 @@ pub fn foo() { } ``` -* how does it work with imports? The same as macros, same error during early resolution - -```rust -mod bar { - pub mod foo { - pub struct Name; - } -} - -mod baz { - pub mod foo { - pub struct Name; - } -} - -use baz::foo; - -pub fn foo() { - use bar::*; - use foo::Name; // `foo` is ambiguous -} -``` +> **NOTE** These ambiguity errors are specific to imports, even though they are +> only observed when those imports are used, having multiple candidates +> available for a given name during later stages of resolution is not +> considered an error, so long as none of the imports themselves are ambiguous, +> there will always be a single unambiguous closest resolution during later +> stages. +> +> ```rust +> mod bar { +> pub struct Name; +> } +> +> mod baz { +> pub struct Name; +> } +> +> use baz::Name; +> +> pub fn foo() { +> use bar::*; +> Name; // resolves to bar::Name +> } +> ``` -r[macro.decl.scope.textual.ambiguity.moreexpandedvsouter] -* it is an error for name bindings from macro expansions to shadow name bindings from outside of those expansions +r[names.resolution.early.imports.ambiguity.moreexpandedvsouter] +It is an error for name bindings from macro expansions to shadow name bindings from outside of those expansions. -```rust +```rust,compile_fail,E0659 macro_rules! name { () => {} } @@ -320,48 +235,105 @@ fn foo() { } ``` +r[names.resolution.early.imports.ambiguity.pathvstextualmacro] +Path-based scope bindings for macros may not shadow textual scope bindings to macros. For bindings from [use declarations], this applies regardless of their [sub-namespace]. -r[names.resolution.early.imports.errors.ambiguity.globvsexpanded] -* Grey Area +```rust,compile_fail,E0659 +#[macro_export] +macro_rules! m2 { + () => {} +} +macro_rules! m { + () => {} +} +pub fn foo() { + m!(); // ERROR `m` is ambiguous + use crate::m2 as m; // in scope for entire function body +} +``` -r[names.resolution.early.macros] +r[names.resolution.early.imports.ambiguity.builtin-attr] +It is an error to use a user defined attribute or derive macro with the same name as a builtin attribute (e.g. inline). -* .visitation-order - * derive helpers - * not visited when resolving derive macros in the parent scope (starting scope) - * derive helpers compat - * always visited - * macro rules bindings (textual scope macros) - * always visited - * modules (path-based scope macros) - * always visited - * macrouseprelude - * not visited in 2018 and later when `#[no_implicit_prelude]` is present - * stdlibprelude - * always visited for macro resolutions - * is it? what about no-std + no-core? - * builtinattrs - * always visited -* .subnamespaces - * macros are split into two subnamespaces, one for bang macros, and the other for attributes and derives. Resolution candidates from the incorrect subnamespace are ignored - * https://doc.rust-lang.org/nightly/reference/names/namespaces.html#r-names.namespaces.sub-namespaces + +```rust,ignore +// myinline/src/lib.rs +use proc_macro::TokenStream; -r[names.resolution.early.macros.errors.reserved-names] +#[proc_macro_attribute] +pub fn inline(_attr: TokenStream, item: TokenStream) -> TokenStream { + item +} +``` -the names cfg and cfg_attr are reserved in the macro attribute sub-namespace + +```rust,ignore +// src/lib.rs +use myinline::inline; +use myinline::inline as myinline; -* https://doc.rust-lang.org/nightly/reference/names/namespaces.html#r-names.namespaces.sub-namespaces +#[myinline::inline] +pub fn foo() {} -r[names.resolution.late] +#[crate::inline] +pub fn bar() {} + +#[myinline] +pub fn baz() {} + +#[inline] // ERROR `inline` is ambiguous +pub fn qux() {} +``` + +r[names.resolution.early.imports.ambiguity.derivehelper] + +Helper attributes may not be used before the macro that introduces them. > [!NOTE] +> rustc currently allows derive helpers to be used before their attribute macro +> introduces them into scope so long as they do not shadow any other attributes +> or derive helpers that are otherwise correctly in scope. This behavior +> deprecated and slated for removal. +> +> ```rust,ignore +> #[helper] // deprecated, hard error in the future +> #[derive(WithHelperAttr)] +> struct Struct { +> field: (), +> } +> ``` > +> For more details, see [Rust issue #79202](https://github.com/rust-lang/rust/issues/79202). + +r[names.resolution.expansion.macros] + +Macros are resolved by iterating through the available scopes until a candidate +is found. Macros are split into two sub-namespaces, one for bang macros, and +the other for attributes and derives. Resolution candidates from the incorrect +sub-namespace are ignored. The available scopes are visited in the following order. + +* derive helpers + * not visited when resolving derive macros in the parent scope (starting scope) +* derive helpers compat +* textual scope macros +* path-based scope macros +* macrouseprelude + * not visited in 2018 and later when `#[no_implicit_prelude]` is present +* stdlibprelude +* builtinattrs + +r[names.resolution.expansion.macros.errors.reserved-names + +the names cfg and cfg_attr are reserved in the macro attribute [sub-namespace]. + +r[names.resolution.late] + +> [!NOTE] > This is a placeholder for future expansion. r[names.resolution.type-dependent] > [!NOTE] -> > This is a placeholder for future expansion. [use glob shadowing]: ../items/use-declarations.md#r-items.use.glob.shadowing @@ -373,3 +345,7 @@ r[names.resolution.type-dependent] [namespaces]: ../names/namespaces.md [scope]: ../names/scopes.md [visibility]: ../visibility-and-privacy.md +[permitted]: name-resolution.md#r-names.resolution.expansion.imports.shadowing +[macro invocations]: ../macros.html#macro-invocation +[path-based scope]: ../macros.html#r-macro.invocation.name-resolution +[sub-namespace]: ../names.namespaces.md#r-names.namespaces.sub-namespaces diff --git a/src/names/namespaces.md b/src/names/namespaces.md index 8050cfd1f7..e3c8794178 100644 --- a/src/names/namespaces.md +++ b/src/names/namespaces.md @@ -117,9 +117,8 @@ This prevents one style from shadowing another. For example, the [`cfg` attribute] and the [`cfg` macro] are two different entities with the same name in the macro namespace, but they can still be used in their respective context. -r[names.namespaces.sub-namespaces.use-shadow] -It is still an error for a [`use` import] to shadow another macro, regardless of their sub-namespaces. -* TODO revisit +> [!NOTE] +> For restrictions on shadowing macro sub-namespaces with [use declaration]s, see [name resolution ambiguity errors]. [`cfg` attribute]: ../conditional-compilation.md#the-cfg-attribute [`cfg` macro]: ../conditional-compilation.md#the-cfg-macro @@ -173,3 +172,5 @@ It is still an error for a [`use` import] to shadow another macro, regardless of [Type aliases]: ../items/type-aliases.md [union]: ../items/unions.md [use declaration]: ../items/use-declarations.md +[name resolution ambiguity errors]: ../names.md/name-resolution.md#r-names.resolution.early.imports.ambiguity.pathvstextualmacro + diff --git a/src/procedural-macros.md b/src/procedural-macros.md index 4395e3db95..ed3f0b23a2 100644 --- a/src/procedural-macros.md +++ b/src/procedural-macros.md @@ -240,6 +240,10 @@ A helper attribute for a derive macro is declared by adding its identifier to th > } > ``` +> [!NOTE] +> +> For helper attribute ambiguity errors, see [name resolution ambiguities]. + r[macro.proc.attribute] ## The `proc_macro_attribute` attribute @@ -440,3 +444,4 @@ their equivalent `#[doc = r"str"]` attributes when passed to macros. [type expressions]: types.md#type-expressions [type]: types.md [union]: items/unions.md +[name resolution ambiguities]: ../names/name-resolution.html#r-names.resolution.expansion.imports.errors.ambiguity