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

Adding non-divergent core::ops overload breaks type inference and default integer type preference #98357

Open
mqudsi opened this issue Jun 21, 2022 · 2 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.

Comments

@mqudsi
Copy link
Contributor

mqudsi commented Jun 21, 2022

This code compiles fine:

use core::ops::Mul;

#[derive(Debug)]
struct Foo(i32);

impl Mul<Foo> for i32 {
    type Output = Foo;

    fn mul(self, other: Foo) -> Self::Output {
        Foo(self * other.0)
    }
}

fn main() {
    let prod1 = 7 * Foo(6);
    assert_eq!(prod1.0, 42);
}

Adding an impl Mul<Foo> for i8 (or any other integer type) breaks the type inference happening on the first line of main() (playground link):

use core::ops::Mul;

#[derive(Debug)]
struct Foo(i32);

impl Mul<Foo> for i32 {
    type Output = Foo;

    fn mul(self, other: Foo) -> Self::Output {
        Foo(self * other.0)
    }
}

impl Mul<Foo> for i8 {
    type Output = Foo;
    
    fn mul(self, other: Foo) -> Self::Output {
        Foo(self as i32 * other.0)
    }
}

fn main() {
    let prod1 = 7 * Foo(6);
    assert_eq!(prod1.0, 42);
}

The stable (1.61.0) compiler gives a better message about what's really happening here as compared to the beta or nightly (1.62.0, 1.63.0) versions. Stable prints this:

error[[E0282]](https://doc.rust-lang.org/stable/error-index.html#E0282): type annotations needed
  --> src/main.rs:24:16
   |
23 |     let prod1 = 7 * Foo(6);
   |         ----- consider giving `prod1` a type
24 |     assert_eq!(prod1.0, 42);
   |                ^^^^^ cannot infer type
   |
   = note: type must be known at this point

error[[E0283]](https://doc.rust-lang.org/stable/error-index.html#E0283): type annotations needed
  --> src/main.rs:23:19
   |
23 |     let prod1 = 7 * Foo(6);
   |                   ^ cannot infer type for type `{integer}`
   |
note: multiple `impl`s satisfying `{integer}: Mul<Foo>` found
  --> src/main.rs:6:1
   |
6  | impl Mul<Foo> for i32 {
   | ^^^^^^^^^^^^^^^^^^^^^
...
14 | impl Mul<Foo> for i8 {
   | ^^^^^^^^^^^^^^^^^^^^

The outputs of all the Mul impl blocks are non-diverging and produce Foo for all inputs. Theoretically, specifying let prod1: Foo = ... instead of let prod1 = ... would do nothing to ameliorate the situation (since either i8 or i32 impl could satisfy the code as written, even with the additional type constraint). However, simply supplying the prod1: Foo type specifier fixes the inference and lets rust's default integer preference succeed: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bf76c0fa2edf27dc656a7bc07ecaab12

Note that this doesn't happen for the commutative equivalent (Foo * numeric rather than numeric * Foo) with the correct Mul impls in place.

rust seems to know that the output is Foo in all cases, because the following emits an error with the correct type:

fn main() {
    let mut prod1 = 7 * Foo(6);
    prod1 = ();
    //assert_eq!(prod1.0, 42);
}
error[[E0271]](https://doc.rust-lang.org/nightly/error-index.html#E0271): type mismatch resolving `<i32 as Mul<Foo>>::Output == ()`
  --> src/main.rs:39:23
   |
39 |     let mut prod1 = 7 * Foo(6);
   |                       ^ type mismatch resolving `<i32 as Mul<Foo>>::Output == ()`
   |
note: expected this to be `Foo`
  --> src/main.rs:23:19
   |
23 |     type Output = Foo;
   |                   ^^^

So there's no reason why : Foo needs to be specified explicitly.

Meta

rustc --version --verbose:

2022-06-20 5750a6aa2777382bf421
@mqudsi mqudsi added the C-bug Category: This is a bug. label Jun 21, 2022
@mqudsi mqudsi changed the title Adding non-divergent core::ops overload breaks literal numeric type inference Adding non-divergent core::ops overload breaks type inference Jun 21, 2022
@mqudsi
Copy link
Contributor Author

mqudsi commented Jul 15, 2022

@rustbot label +A-inference +C-bug +T-core

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-inference Area: Type inference T-core Relevant to the core team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. labels Jul 15, 2022
@mqudsi mqudsi changed the title Adding non-divergent core::ops overload breaks type inference Adding non-divergent core::ops overload breaks default integer type preference Jul 18, 2022
@mqudsi mqudsi changed the title Adding non-divergent core::ops overload breaks default integer type preference Adding non-divergent core::ops overload breaks type inference and default integer type preference Jul 18, 2022
@apiraino apiraino added needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. and removed T-core Relevant to the core team, which will review and decide on the PR/issue. labels Mar 11, 2025
@apiraino
Copy link
Contributor

Linking discussion on Zulip for tracking purposes

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.
Projects
None yet
Development

No branches or pull requests

3 participants