Skip to content
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

Can't infer both sides of a Result when using ? #138579

Open
richo opened this issue Mar 16, 2025 · 5 comments
Open

Can't infer both sides of a Result when using ? #138579

richo opened this issue Mar 16, 2025 · 5 comments
Labels
A-inference Area: Type inference C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@richo
Copy link
Contributor

richo commented Mar 16, 2025

I tried this code:

trait Trait {}
impl Trait for ((), ()) {}

fn returns_impl_whatever(condition: bool) -> Result<impl Trait, impl Trait> {
    if condition {
        Err(((), ()))?;
    }

    Ok(((), ()))
}

I expected to see this happen: I believe this should compile, both T and E should be inferrable by the compiler.

Instead, this happened:

   Compiling impl-whatever v0.1.0 (/private/tmp/impl-whatever)
error[E0282]: type annotations needed
 --> src/lib.rs:9:5
  |
9 |     Ok(((), ()))
  |     ^^ cannot infer type of the type parameter `E` declared on the enum `Result`
  |
help: consider specifying the generic arguments
  |
9 |     Ok::<((), ()), E>(((), ()))
  |       +++++++++++++++

For more information about this error, try `rustc --explain E0282`.
error: could not compile `impl-whatever` (lib) due to 1 previous error

I also tried a couple of other things. An explicit return Err(...) in the branch typechecks fine. If I try something like:

        if let Err(e) = Err(((), ())) {
            return Err(e);
        }

I get an error telling me I need to specify the type of T. So it appears that it's able to infer either type, but not both at the same time.

Meta

Tried on stable and nightly

rustc --version --verbose:

rustc 1.85.0 (4d91de4e4 2025-02-17)
binary: rustc
commit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688
commit-date: 2025-02-17
host: aarch64-apple-darwin
release: 1.85.0
LLVM version: 19.1.7


rustc 1.87.0-nightly (4d30011f6 2025-03-15)
binary: rustc
commit-hash: 4d30011f6c616be074ba655a75e5d55441232bbb
commit-date: 2025-03-15
host: aarch64-apple-darwin
release: 1.87.0-nightly
LLVM version: 20.1.0
@richo richo added the C-bug Category: This is a bug. label Mar 16, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Mar 16, 2025
@bjorn3
Copy link
Member

bjorn3 commented Mar 17, 2025

The return type could be inferred as Result<(), MyError> if there exists an impl From<()> for MyError. This is because ? uses .into() on the error value before returning the error.

@richo
Copy link
Contributor Author

richo commented Mar 17, 2025

Sorry I'm not sure if I understand your comment. I'm aware that ? calls .into() to coerce the value, but .into() can coerce Self into Self. I had a quick look at the implementation but got lost pretty fast.

With that said, I explored what you said a little and it appears that the compiler is only using one site to attempt to expand the whole return signature instead of combining everything in the body, changing E to a different trait in the case where I manually unwrap the Err case still gives me the error that a type is required for T, even though there is one to be inferred from the Ok(..) at the end.

@bjorn3
Copy link
Member

bjorn3 commented Mar 17, 2025

but .into() can coerce Self into Self.

Sure, but it could also turn it into another error type and because you are using RPIT (return position impl trait) such a different type would also be completely valid. And multiple valid types results in an error.

@richo
Copy link
Contributor Author

richo commented Mar 17, 2025

I'm not sure that is what is happening here? There is only one possible type. Even if that were the case, this code

trait Trait {}
impl Trait for () {}

trait MyError {}
impl MyError for () {}

fn returns_impl_whatever(condition: bool) -> Result<impl Trait, impl MyError> {
    if condition {
        if let Err(e) = Err(()) {
            return Err(e);
        }
    }

    Ok(())
}

Does not invoke .into(), since it doesn't use ?, ad results in

   Compiling impl-whatever v0.1.0 (/private/tmp/impl-whatever)
error[E0282]: type annotations needed
 --> src/lib.rs:9:25
  |
9 |         if let Err(e) = Err(()) {
  |                         ^^^ cannot infer type of the type parameter `T` declared on the enum `Result`
  |
help: consider specifying the generic arguments
  |
9 |         if let Err(e) = Err::<T, ()>(()) {
  |                            +++++++++

For more information about this error, try `rustc --explain E0282`.
error: could not compile `impl-whatever` (lib) due to 1 previous error

Despite the fact that T's type is plainly ().

@bjorn3
Copy link
Member

bjorn3 commented Mar 18, 2025

In that case there is nothing fixing the T for the Err(()) value. It is only fixed for return Err(e).

@lolbinarycat lolbinarycat added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-inference Area: Type inference T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Mar 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inference Area: Type inference C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants