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

Rust incorrectly infers the generic type if used with failure::ResultExt and the try macro. #55928

Open
marmistrz opened this issue Nov 13, 2018 · 4 comments
Labels
A-inference Area: Type inference A-traits Area: Trait system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@marmistrz
Copy link
Contributor

marmistrz commented Nov 13, 2018

When trying to compile the following code, the compiler will incorrectly infer the type as ().
It shouldn't happen, since the returned type is Result<U, _> and context doesn't change the generic value type in Result.

extern crate serde;
extern crate failure;
extern crate reqwest;
use serde::{Deserialize, Serialize};
use failure::{Fallible, ResultExt, format_err};

fn post<T, U>(json: T) -> Fallible<U>
where
    T: Serialize,
    for<'a> U: Deserialize<'a>,
{
    let client = reqwest::Client::new();
    let url = "http://someurl.com";
    let mut resp = client.post(url).json(&json).send()?;
    let resp_content: Result<U, String> = resp.json().context("Bad JSON")?;
    resp_content.map_err(|e| format_err!("Provider replied: {:?}", e))
}

fn run() -> Fallible<()> {
    // works fine, the error is:
    //      error[E0282]: type annotations needed
    //let ret = post("foo").expect("blah");

    // This incorrectly infers the type `()` instead of requiring type annotations. 
    // The compilation fails in the `println` line
    //      error[E0277]: `()` doesn't implement `std::fmt::Display`
    let ret = post("foo").context("blah")?;

    // Here the typecheck succeeds
    // let ret: String = post("foo").context("blah")?;
    println!("{}", ret);
    Ok(())
}

fn main() {
    run().unwrap();
}

The dependencies in Cargo.toml:

[dependencies]
failure = "0.1"
serde = "1.0"
reqwest = "0.9"

This happens on both Rust 1.30.1 and 1.32.0-nightly (65204a9 2018-11-12)
It is clearly a compiler bug: if both () and String are correct types, then the type inference should fail.

@marmistrz marmistrz changed the title Rust incorrectly infers the type Rust incorrectly infers the generic type if used with failure::ResultExt and the try macro. Nov 13, 2018
@jonas-schievink jonas-schievink added A-traits Area: Trait system T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 27, 2019
@kadiwa4
Copy link
Contributor

kadiwa4 commented Jan 28, 2023

Smaller example (play):

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let bar = foo()?;
    println!("{bar}");
    Ok(())
}

fn foo<T: Default>() -> Result<T, Box<dyn Error>> {
    Ok(T::default())
}

Gives (rust 2023-01-27):

error[E0277]: `()` doesn't implement `std::fmt::Display`
 --> src/main.rs:5:16
  |
5 |     println!("{bar}");
  |                ^^^ `()` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `()`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.

@rustbot label: +A-inference

@ijackson
Copy link
Contributor

An example involving iterators:

fn mkv(_: &i32) -> Result<(), ()> { Ok(()) }

pub fn f() -> Result<(), ()> {
    let vs; // adding `: Vec<_>` makes it work
    vs = [].iter().map(mkv).collect::<Result<_, _>>()?;
    for _ in vs {}
    Ok(())
}

In all these cases, I think the error message ought to tell us that type annotations are required.

@ijackson
Copy link
Contributor

@rustbot label +C-bug

@rustbot rustbot added the C-bug Category: This is a bug. label Apr 29, 2024
@ijackson
Copy link
Contributor

To be more formal:

Steps to reproduce

Try to compile #55928 (comment) eg on the playground.

Actual output

error[E0277]: `()` is not an iterator
 --> src/lib.rs:6:14
  |
6 |     for _ in vs {}
  |              ^^ `()` is not an iterator
  |
  = help: the trait `Iterator` is not implemented for `()`

This is wrong because the type of vs is unknown, rather than being ().

Expected output

Something like this:

5    |     vs = [].iter().map(mkv).collect::<Result<_, _>>()?;
     |                             -------   - type must be known at this point

as seen in this modified example without the ?: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=13e249c92a7f08df8d145fd3a385f6ce

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inference Area: Type inference A-traits Area: Trait system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants