You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Cannot refer to item inside function from module inside function #79260
error[E0425]: cannot find function `bar` in this scope
--> src/main.rs:6:13
|
6 | bar();
| ^^^ not found in this scope
warning: unused import: `super::*`
--> src/main.rs:3:13
|
3 | use super::*;
| ^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
Also reproduces on current nightly (as of 2020-11-21).
I suspect that what's going on is that use super::* doesn't actually resolve to the scope inside fn main, but instead the surrounding scope fn main is itself in.
I tried to search around other issues to find whether this is expected.
This is a super weird special case. It seems plausible (at least to me) that use super::* from a module inside a function should bring other items local to that function into scope.
Reacted by Diana, Andrew Hlynskyi and Alisa Sireneva
I'm not convinced it would break that code; bar is already a valid identifier inside fn main and so I would argue use super::*; inside main::foo should resolve fn bar in that case too.
I can find another example of this which is easy to reproduce and seems like a much more likely case to trigger!
Write a doctest which declares a module inside it:
/// ```rust/// struct Foo;////// mod foobar {/// use super::*;////// fn bar() -> Foo { Foo }/// }////// ```fnfoo(){}
This doctest will fail to compile:
---- src/lib.rs - foo (line 1) stdout ----
error[E0412]: cannot find type `Foo` in this scope
--> src/lib.rs:7:17
|
8 | fn bar() -> Foo { Foo }
| ^^^ not found in this scope
error[E0425]: cannot find value `Foo` in this scope
--> src/lib.rs:7:23
|
8 | fn bar() -> Foo { Foo }
| ^^^ not found in this scope
I would guess that the doctest framework wraps the doctest code inside a function, so we see exactly the same issue as in my original report.
At the moment it always prints "outer bar", but if we fixed my original example this would either be ambiguous or print "inner bar", I asssume.
(We could perhaps make it backwards compatble by letting items outside the function take precedence, and then switch the order to prefer items inside the function on an edition change.)
I also wouldn't call this a bug. Everything is working as it originally is intended to. super refers to the parent module, not the parent item scope. Functions allow defining items that are local to the function and a module is non-local, even if it's declared inside the function.
IMO, if you need a module, just like if you need an impl, it should not be defined in a function.
We probably need some way to refer to block-that-are-parent-of-modules, but super is not that way. super has a well-defined meaning - parent mod, similarly to how self is the current module and not current block or something.
Besides being a breaking change, path resolving to an unnamed block is some new possibility that will cause a number of subsequent issues.
What would happen here, for example
usesuperas foo;// referes to a blocklet bar = foo;
My preferred solution to this issue is probably #[transparent] mod foo { ... } that prevent the module from introducing a "name barrier".
It's something that is more useful in various situations, and fits more naturally into the existing system.
UPD: Example.
structS;#[transparent]mod m {fnf(){let s = S;// OK}}
#[transparent] attribute is a very compelling proposal 👍. It would be a shame that it would be needed in some doctest examples. Still great as a 100% backwards-compatible opt-in design.
I wrote a PR to experiment with what I proposed above (#79309) - I've added an alternatives section with #[transparent].
Here is a real use case for this. I'd like to generate a child module in a proc macro where I'd place the definition of the builder stuct (for privacy of its fields). The builder's fields should reference types from the surrounding scope.
fnexample(){structPassword(String);// Suppose a `#[builder]` proc macro was placed on this structstructUser{password:Password,}// This module is generated by the proc-macromod user_builder {usesuper::*;pub(super)structUserBuilder{password:Option<Password>,}}}
Another, similar use case for this is with a new feature in our HTTP server Dropshot called API traits. Applying this proc macro on the trait causes a bunch of support functions (and soon at least one macro) to be generated. The options are:
Put the generated items at the top-level, polluting the top-level namespace in an undesirable way.
Hang the items off of a type (e.g. enum SupportFunctions {}, then impl SupportFunctions { ... }). This would work for functions but not for macros.
Put them in a support module. That's what we've chosen, but this runs into the issue described here.
The proposal to have a way to mark a module as transparent in this comment above would solve this issue, and I think it would be worth pursuing.
@sunshowers I've since discovered another option to workaround this problem, if your goal is to make the generated symbols private. You can just randomize the names of your generated symbols using some simple randomness source like std's RandomState (example).
This way the users won't be able to use the generated names, and even if they do, they won't be able to compile/re-compile the code if they do that. I also recommend marking these symbols with #[deprecated] to further complicate their usage.
@sunshowers I've since discovered another option to workaround this problem, if your goal is to make the generated symbols private. You can just randomize the names of your generated symbols using some simple randomness source like std's RandomState (example).
This way the users won't be able to use the generated names, and even if they do, they won't be able to compile/re-compile the code if they do that. I also recommend marking these symbols with #[deprecated] to further complicate their usage.
Ah, in my case I'd like the generated symbols to be public.
I'm assuming this change will require updates to each of the various stages of name resolution in order to be feature complete, but off of the top of my head I think https://github.com/yaahc/rust/blob/8111a2d6da405e9684a8a83c2c9d69036bf23f12/compiler/rustc_resolve/src/ident.rs#L344-L345 is where the change for late resolution (most resolution) would need to occur. I'm pretty sure I just add an unstable attribute, check if its present on a module when resolving idents in lexical scope, and if it is we simply don't abandon the ribs yet.
I have no idea what this would look like for type dependent name resolution since I haven't really explored that much yet.
Footnotes
This is motivated by me wanting a better understanding of name resolution as part of my effort to document it in the rust reference. I'm not however interested in pushing for stabilization or any of the actual hard work associated with making it generally usable ↩
Reacted by Esteban Kuber, Andrew Hlynskyi and Dylan Ede
Activity
davidhewitt commentedon Nov 21, 2020
(If the lang team agrees that this code should compile, I'm willing to attempt a patch to fix.)
jonas-schievink commentedon Nov 21, 2020
Note that fixing this would break this code:
davidhewitt commentedon Nov 21, 2020
I'm not convinced it would break that code;
baris already a valid identifier insidefn mainand so I would argueuse super::*;insidemain::fooshould resolvefn barin that case too.davidhewitt commentedon Nov 21, 2020
I can find another example of this which is easy to reproduce and seems like a much more likely case to trigger!
Write a doctest which declares a module inside it:
This doctest will fail to compile:
I would guess that the doctest framework wraps the doctest code inside a function, so we see exactly the same issue as in my original report.
davidhewitt commentedon Nov 21, 2020
Ah, but I think this variation of @jonas-schievink 's example will break:
At the moment it always prints
"outer bar", but if we fixed my original example this would either be ambiguous or print"inner bar", I asssume.(We could perhaps make it backwards compatble by letting items outside the function take precedence, and then switch the order to prefer items inside the function on an edition change.)
Havvy commentedon Nov 22, 2020
I am against changing the meaning of
super.I also wouldn't call this a bug. Everything is working as it originally is intended to.
superrefers to the parent module, not the parent item scope. Functions allow defining items that are local to the function and a module is non-local, even if it's declared inside the function.IMO, if you need a module, just like if you need an impl, it should not be defined in a function.
petrochenkov commentedon Nov 22, 2020
We probably need some way to refer to block-that-are-parent-of-modules, but
superis not that way.superhas a well-defined meaning - parent mod, similarly to howselfis the current module and not current block or something.Besides being a breaking change, path resolving to an unnamed block is some new possibility that will cause a number of subsequent issues.
What would happen here, for example
petrochenkov commentedon Nov 22, 2020
My preferred solution to this issue is probably
#[transparent] mod foo { ... }that prevent the module from introducing a "name barrier".It's something that is more useful in various situations, and fits more naturally into the existing system.
UPD: Example.
davidhewitt commentedon Nov 22, 2020
#[transparent]attribute is a very compelling proposal 👍. It would be a shame that it would be needed in some doctest examples. Still great as a 100% backwards-compatible opt-in design.I wrote a PR to experiment with what I proposed above (#79309) - I've added an alternatives section with
#[transparent].AndreySmirnov81 commentedon Sep 25, 2023
I would like to see this result:
1. For Rust before 2024 edition
2. For Rust before 2024 edition
3. For Rust before 2024 edition
4. For Rust before 2024 edition
5. For Rust 2024 edition
AndreySmirnov81 commentedon Oct 26, 2023
Alternatively, for this non-working example:
we can use this:
However, there were difficulties with rust-analyzer: rust-lang/rust-analyzer#15805
Veetaha commentedon Aug 11, 2024
Here is a real use case for this. I'd like to generate a child module in a proc macro where I'd place the definition of the builder stuct (for privacy of its fields). The builder's fields should reference types from the surrounding scope.
I made an article about this problem here: https://elastio.github.io/bon/blog/the-weird-of-function-local-types-in-rust
sunshowers commentedon Oct 12, 2024
Another, similar use case for this is with a new feature in our HTTP server Dropshot called API traits. Applying this proc macro on the trait causes a bunch of support functions (and soon at least one macro) to be generated. The options are:
enum SupportFunctions {}, thenimpl SupportFunctions { ... }). This would work for functions but not for macros.The proposal to have a way to mark a module as transparent in this comment above would solve this issue, and I think it would be worth pursuing.
Veetaha commentedon Oct 12, 2024
@sunshowers I've since discovered another option to workaround this problem, if your goal is to make the generated symbols private. You can just randomize the names of your generated symbols using some simple randomness source like std's
RandomState(example).This way the users won't be able to use the generated names, and even if they do, they won't be able to compile/re-compile the code if they do that. I also recommend marking these symbols with
#[deprecated]to further complicate their usage.sunshowers commentedon Oct 12, 2024
Ah, in my case I'd like the generated symbols to be public.
yaahc commentedon Oct 13, 2025
I'm thinking about trying to go and implement
#[transparent]for fun and education1. This came across my path when @estebank's coworker ran into the same issue #t-lang-docs/reference > name resolution @ 💬I'm assuming this change will require updates to each of the various stages of name resolution in order to be feature complete, but off of the top of my head I think https://github.com/yaahc/rust/blob/8111a2d6da405e9684a8a83c2c9d69036bf23f12/compiler/rustc_resolve/src/ident.rs#L344-L345 is where the change for late resolution (most resolution) would need to occur. I'm pretty sure I just add an unstable attribute, check if its present on a module when resolving idents in lexical scope, and if it is we simply don't abandon the ribs yet.
I think for early resolution (imports & macros) the change would need to happen in hygenic_lexical_parent, basically add another check similar to https://github.com/yaahc/rust/blob/8111a2d6da405e9684a8a83c2c9d69036bf23f12/compiler/rustc_resolve/src/ident.rs#L236-L238 which instead looks for Def(mod) module kinds that are transparent, which would then be treated as a valid lexical parent.
I have no idea what this would look like for type dependent name resolution since I haven't really explored that much yet.
Footnotes
This is motivated by me wanting a better understanding of name resolution as part of my effort to document it in the rust reference. I'm not however interested in pushing for stabilization or any of the actual hard work associated with making it generally usable ↩