resolve: Report more visibility-related early resolution ambiguities for imports by petrochenkov · Pull Request #149596 · rust-lang/rust (original) (raw)
Let's talk through what we're doing here. Today, this is an error:
mod m { pub struct S {} } macro_rules! mac { () => { struct S {} }}
pub use m::*; mac!();
pub use S as _;
//~^ error[E0659]: S is ambiguous
Why? What do the rules say? First:
r[names.resolution.expansion.imports.ambiguity.intro]
Some situations are an error when there is an ambiguity as to which macro definition,usedeclaration, or module an import or macro invocation's name refers to. This happens when there are two name candidates that do not resolve to the same entity where neither candidate is [permitted] to shadow the other.
Are there two name candidates? Yes; the struct in m and the one defined via the macro invocation.
Do they resolve to the same entity? No; they have two distinct definitions.
Is one permitted to shadow the other? Let's see.
r[names.resolution.expansion.imports.shadowing.shared-scope]
Shadowing of names introduced viausedeclarations within a single scope is permitted in the following situations:
- [
useglob shadowing]- [Macro textual scope shadowing]
This is indeed use glob shadowing, which says:
r[items.use.glob.shadowing]
Items and named imports are allowed to shadow names from glob imports in the same [namespace]. That is, if there is a name already defined by another item in the same namespace, the glob import will be shadowed.
So this would be allowed normally. If S were used in a function body during primary name resolution (late resolution), it would be accepted). But S is used in an import, which is resolved during expansion-time resolution (early resolution).
r[names.resolution.expansion.expansion-order-stability]
The resolution of names must be stable. After expansion, names in the fully expanded AST must resolve to the same definition regardless of the order in which macros are expanded and imports are resolved.
Because the struct S is introduced by mac!(), it might not be visible when S in pub use S as _ is first resolved. If the compiler resolves S before expanding mac!(), it would see m::S (from the glob). If it expands mac!() first, it would see the struct from the macro invocation. Since the resolution depends on the order of expansion, it violates the stability rule and reports an error.
Conversely, this is accepted today:
mod m { pub struct S {} } macro_rules! mac { () => { use m::S; }}
pub use m::*; mac!();
pub use S as _; //~ OK
Why? Are there two name candidates? Yes. But now they resolve to the same entity. The ambiguity still exists, but because both resolution paths lead to the same definition, the compiler currently considers it benign and accepts it.
Should we lint about this? @petrochenkov gives three rules:
- The two declarations are ambiguous.
- The two declarations have the same definition but different visibilities.
min(vis(decl1), vis(import)) != min(vis(decl2), vis(import))
Taking them in turn:
One. What's ambiguous mean? As I read this (and, @petrochenkov or @yaahc, correct me if I'm wrong), this is suggesting that two declarations are ambiguous when they are candidates for the same name in the same scope and neither is permitted to shadow the other or the resolution order would make the shadowing unpredictable. I.e., they are ambiguous when we would give an error if the definitions of those declarations are not the same.
So these declarations are ambiguous.
Two. They have the same definition. Do they have different visibilities? Yes. pub (from pub use m::*) vs private (from use m::S).
Three. The visibility of the import is pub (from pub use S as _), so the minimum visibility will be the visibility of each declaration. As above, the declarations have different visibilities.
QED. We lint this case.
Net-net, with this FCW, we're reducing the scope of what we consider to be a benign ambiguity.
Based on that understanding...
@rfcbot reviewed