Skip to content

Integer literal type inference causes weird behavior including strange compiler diagnostic (but the suggestion is working). #129360

Open
@VictorArcium

Description

@VictorArcium

Code

use std::ops::Mul;

struct Stuff{
    a: i128
}


impl Mul<i64> for Stuff {
    type Output = Stuff;
    fn mul (self, other: i64) -> Stuff {
        Stuff{a: self.a * (other as i128)}
    }
}
impl Mul<i32> for Stuff {
    type Output = Stuff;
    fn mul (self, other: i32) -> Stuff {
        Stuff{a: self.a * (other as i128)}
    }
}


fn main() {
    let stuff = Stuff{a: 42};

    let y = stuff * 2; // Note: if I specify y's type or 2's type, it compiles.

    println!("{:?}", y.a);
}

Current output

error[E0282]: type annotations needed
  --> src/main.rs:25:9
   |
25 |     let y = stuff * 2;
   |         ^
26 |     
27 |     println!("{:?}", y.a);
   |                      - type must be known at this point
   |
help: consider giving `y` an explicit type
   |
25 |     let y: /* Type */ = stuff * 2;
   |          ++++++++++++

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

Desired output

It should compile.

Rationale and extra context

Following https://doc.rust-lang.org/reference/expressions/literal-expr.html#integer-literal-expressions I think the compiler should be able to infer that 2 is an i32 and compile my program.

Alternatively, regardless of 2's type, y can only be a Stuff, so this precision should be unnecessary and the compiler should compile my program.

Other cases

The following modifications compile:

  • The program modified according to the compiler's suggestion (adding Stuff as y's type). (code below)
  • Displaying y instead of y.a. (code below)
  • Changing 2 to 2i32 or 2i64. (no code below)

The compiler suggestion (compiles):

use std::ops::Mul;

struct Stuff{
    a: i128
}


impl Mul<i64> for Stuff {
    type Output = Stuff;
    fn mul (self, other: i64) -> Stuff {
        Stuff{a: self.a * (other as i128)}
    }
}
impl Mul<i32> for Stuff {
    type Output = Stuff;
    fn mul (self, other: i32) -> Stuff {
        Stuff{a: self.a * (other as i128)}
    }
}


fn main() {
    let stuff = Stuff{a: 42};

    let y: Stuff = stuff * 2; // This is the change the compiler suggests, and it works.

    println!("{:?}", y.a);
}

Displaying y instead of y.a (compiles too):

use std::ops::Mul;

#[derive(Debug)] // First change in this code snippet
struct Stuff{
    a: i128
}


impl Mul<i64> for Stuff {
    type Output = Stuff;
    fn mul (self, other: i64) -> Stuff {
        Stuff{a: self.a * (other as i128)}
    }
}
impl Mul<i32> for Stuff {
    type Output = Stuff;
    fn mul (self, other: i32) -> Stuff {
        Stuff{a: self.a * (other as i128)}
    }
}


fn main() {
    let stuff = Stuff{a: 42};

    let y = stuff * 2;

    println!("{:?}", y); // 2nd change, I display y instead of y.a
}

Rust Version

rustc 1.80.1 (3f5fd8dd4 2024-08-06)
binary: rustc
commit-hash: 3f5fd8dd41153bc5fdca9427e9e05be2c767ba23
commit-date: 2024-08-06
host: x86_64-unknown-linux-gnu
release: 1.80.1
LLVM version: 18.1.7

Anything else?

It also causes the same compiler diagnostic on 1.79.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-inferenceArea: Type inferenceC-discussionCategory: Discussion or questions that doesn't represent real issues.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions