I tried to document this code:
pub use std::char;
/// Foooooooo
///
/// [`char`] [`type@char`] [`std::char`]
pub struct Foo;
I expected to see this happen:
[`char`] and [`std::char`] points to module page of char[`type@char`] points to char primitive pageInstead, this happened:
[`type@char`] points to module page of charrustc --version --verbose:
rustc 1.46.0-nightly (0cd7ff7dd 2020-07-04)
binary: rustc
commit-hash: 0cd7ff7ddfb75a38dca81ad3e76b1e984129e939
commit-date: 2020-07-04
host: x86_64-unknown-linux-gnu
release: 1.46.0-nightly
LLVM version: 10.0
I think the problem is that modules are in the type namespace, so type@char is doing exactly what you are asking it for (the char module, since it has higher priority). Since the built-in types scope is the lowest precedence, perhaps there needs to be a way to access it directly? Maybe something like prim@char or builtin@char?
Or maybe type@ should skip over modules, and keep looking deeper in the scope hierarchy? (cc #58699)
Oh hmm I see what you mean. There are only three namespaces: Types, values, and macros. Since modules and types are in the same namespace, it uses the first one it finds.
Maybe we could work around this by first resolving in an empty module (so it has nothing in scope), then only falling back to the current scope if we don't find something there?
I'm surprised this hasn't caused trouble before now. Does the root of std never use the char primitive?
I don't know how it works, but the resolver is smart enough to know that char in a type context won't match a module (like let x: char = 'c', the char won't match a module named "char". Maybe it has something to do with late-resolution smart_resolve_path which has a PathSource to give it some context?
Adding that would be more of @Manishearth's area ... I can give it a shot though.
It looks like smart_resolve_path doesn't even return a resolution and only records it for later use ... I'm going to go with the empty module workaround and we can fix it later if it turns out to cause issues.
The double resolve seems hacky. We could probably make type@ specifically not resolve to a module, perhaps?
Also, note that modules are in all namespaces
The double resolve seems hacky. We could probably make type@ specifically not resolve to a module, perhaps?
How would that work? We could skip any module resolutions, but since modules have precedence over types I don't know how we'd get the primitive type to resolve afterwards.
Oh hold on we have a is_primitive function already. We can look at PRIMITIVES directly.
This doesn't have anything to do with primitives, this has to do with glob imports vs non-glob imports. You can get the same issue with the following:
pub mod ch {}
pub mod inner {
pub struct ch {
x: bool
}
}
use inner::*;
/// [`type@ch`] [`mod@ch`] [`ch`]
pub fn foo() {
}
This also doesn't have to do with modules being "resolved first" or anything. Things that are non-glob in-scope are resolved first.
Actually, my bad: This _does_ have to do with primitives in the sense that primitives are the only thing that still work when shadowed because of some special code in smart_resolve_path checking the primitive tables.
I think when the string matches one of the primitive table entries for TypeNS lookups that were explicitly annotated with type@ and resolve to a module we can hack this in.
In the future we might want to tighten up this code so that e.g. struct@foo can't resolve to an enum, etc.
@Manishearth do you mind if I fix https://github.com/rust-lang/rust/issues/58699 at the same time and resolve primitives even before modules? That seems like the less surprising behavior to me, the module can still be disambiguated with std::char.
@jyn514 I'm a bit wary of doing this because having the char module in scope is not a common situation, and it will get weirder for people who have a different char module.
I think we should resolve to whatever is in scope, but if type@ is explicitly mentioned we can then force resolve to the primitive.