Skip to content

crates are considered for fn() -> type name resolution if they have generic parameters #139095

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
jyn514 opened this issue Mar 29, 2025 · 4 comments
Labels
A-resolve Area: Name/path resolution done by `rustc_resolve` specifically C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged.

Comments

@jyn514
Copy link
Member

jyn514 commented Mar 29, 2025

I tried this code:

extern crate libc as usize;
extern crate libc as Option;
#[usize::inner] //~ ERROR could not find `inner` in `usize`
fn foo() {}
fn bar() -> usize { 0 } // OK
fn baz() -> Option<T> { 0 } //~ ERROR type arguments not allowed on crate

I expected to see this happen: usize and Option should both consistently resolve to the crate, or they should consistently resolve to the builtin type.

Instead, this happened: In return position, usize resolves to the builtin type and Option resolves to the crate.
In attribute position, usize resolves to the crate (which seems reasonable, because associated macros aren't a thing.)

Meta

rustc --version --verbose: 1.87.0-nightly (2025-03-28 920d95eaf23d7eb6b415)

@rustbot label A-resolve

@jyn514 jyn514 added the C-bug Category: This is a bug. label Mar 29, 2025
@rustbot rustbot added needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. A-resolve Area: Name/path resolution done by `rustc_resolve` specifically labels Mar 29, 2025
@jyn514 jyn514 changed the title crates are not considered for fn() -> type name resolution, except if they have generic parameters crates are considered for fn() -> type name resolution if they have generic parameters Mar 29, 2025
@jyn514
Copy link
Member Author

jyn514 commented Mar 29, 2025

cc @petrochenkov

@compiler-errors
Copy link
Member

I think this is two things entangled into what seems like one bug?

Specifically, I don't think the preference for crates over types is because of the presence of generic args. I think we just prefer primitives over crate names in resolution.

Specifically, we should be hitting a resolver error before even trying to compute the generics of Option<T> when Option is defined as a crate name:

error[E0573]: expected type, found crate `Option`
 --> src/lib.rs:6:13
  |
6 | fn baz() -> Option<T> { 0 }
  |             ^^^^^^^^^ not a type

Am I missing something, b/c I can't seem to reproduce the error "type arguments not allowed on crate" with the code you shared?

I'm reading between the lines, but did you happen to modify this example to use Option when filing this bug, but previously were experimenting with something like the following code?

extern crate libc as usize;

extern crate libc as isize;

fn bar() -> usize { 0 } // OK

fn baz() -> isize<i32> { 0 } // type arguments are not allowed on crate `libc`

According to that error, it may look like baz's return type is resolving to libc as isize, but I think that's just a diagnostic bug, since we later go on to note:

help: primitive type `isize` doesn't have generic parameters
  |
4 - fn baz() -> isize<i32> { 0 } //~ ERROR type arguments not allowed on crate
4 + fn baz() -> isize { 0 } //~ ERROR type arguments not allowed on crate

And indeed, the "path res" (the resolution) for the full path isize<i32> is indeed PrimTy, even if the "segment res" (resolution of just the path segment) is the crate. That's probably just a bug.

@compiler-errors
Copy link
Member

compiler-errors commented Mar 30, 2025

// In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
// don't report an error right away, but try to fallback to a primitive type.
// So, we are still able to successfully resolve something like
//
// use std::u8; // bring module u8 in scope
// fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8
// u8::max_value() // OK, resolves to associated function <u8>::max_value,
// // not to nonexistent std::u8::max_value
// }
//
// Such behavior is required for backward compatibility.
// The same fallback is used when `a` resolves to nothing.
PathResult::Module(ModuleOrUniformRoot::Module(_)) | PathResult::Failed { .. }
if (ns == TypeNS || path.len() > 1)
&& PrimTy::from_name(path[0].ident.name).is_some() =>
{
let prim = PrimTy::from_name(path[0].ident.name).unwrap();
let tcx = self.r.tcx();
let gate_err_sym_msg = match prim {
PrimTy::Float(FloatTy::F16) if !tcx.features().f16() => {
Some((sym::f16, "the type `f16` is unstable"))
}
PrimTy::Float(FloatTy::F128) if !tcx.features().f128() => {
Some((sym::f128, "the type `f128` is unstable"))
}
_ => None,
};
if let Some((sym, msg)) = gate_err_sym_msg {
let span = path[0].ident.span;
if !span.allows_unstable(sym) {
feature_err(tcx.sess, sym, span, msg).emit();
}
};
PartialRes::with_unresolved_segments(Res::PrimTy(prim), path.len() - 1)
}

This is why we prefer primitives > crate resolutions. So I don't think this has to do with the presence of generic parameters, and that wonky generic parameter error message that mentions "type arguments are not allowed on crate" which I shared above which is just a diagnostics bug which I've fixed in #139127.

LMK if I'm missing something critical here

@jyn514
Copy link
Member Author

jyn514 commented Mar 30, 2025

I'm reading between the lines, but did you happen to modify this example to use Option when filing this bug, but previously were experimenting with something like the following code?

you're absolutely right, that comment is left over from an old version of the code. nice catch, thanks.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Apr 14, 2025
…i-obk

Fix up partial res of segment in primitive resolution hack

There is a hack in the resolver:

```
// In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
// don't report an error right away, but try to fallback to a primitive type.
```

This fixes up the resolution for primitives which would otherwise resolve to a module, but we weren't also updating the res of the path segment, leading to weird diagnostics.

We explicitly call `self.r.partial_res_map.insert` instead of `record_partial_res` b/c we have recorded a partial res already, and we specifically want to override it.

cc rust-lang#139095 (comment)
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Apr 14, 2025
Rollup merge of rust-lang#139127 - compiler-errors:prim-ty-hack, r=oli-obk

Fix up partial res of segment in primitive resolution hack

There is a hack in the resolver:

```
// In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
// don't report an error right away, but try to fallback to a primitive type.
```

This fixes up the resolution for primitives which would otherwise resolve to a module, but we weren't also updating the res of the path segment, leading to weird diagnostics.

We explicitly call `self.r.partial_res_map.insert` instead of `record_partial_res` b/c we have recorded a partial res already, and we specifically want to override it.

cc rust-lang#139095 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-resolve Area: Name/path resolution done by `rustc_resolve` specifically C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged.
Projects
None yet
Development

No branches or pull requests

3 participants