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

Compiler not realizing an impl is more specific than another when using const generic bounds #138308

Closed
Absobel opened this issue Mar 10, 2025 · 7 comments
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues. F-generic_const_exprs `#![feature(generic_const_exprs)]`

Comments

@Absobel
Copy link
Contributor

Absobel commented Mar 10, 2025

I tried this code which creates a register and an operation to decrease it's value by one or change it's value to another register. All of that at compile time.

#![feature(generic_const_exprs)]

trait _DecOr<E> {
    type O;
}

struct R1<const N: usize>;

impl<E> _DecOr<E> for R1<0> {
    type O = E;
}

impl<const N:usize, E> _DecOr<E> for R1<N> where [(); N - 1]: {
    type O = R1<{N - 1}>;
}

I expected it to work with no errors.

Instead, this happened:

   Compiling mintypesky v0.1.0 (/home/absobel/Mir/GitRepos/MinTypeSky)
error[E0080]: evaluation of `<R1<0> as _DecOr<!0>>::{constant#0}` failed
  --> src/main.rs:25:55
   |
25 | impl<const N:usize, E> _DecOr<E> for R1<N> where [(); N - 1]: {
   |                                                       ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow

error[E0080]: evaluation of `<R1<0> as _DecOr<E>>::{constant#0}` failed
  --> src/main.rs:25:55
   |
25 | impl<const N:usize, E> _DecOr<E> for R1<N> where [(); N - 1]: {
   |                                                       ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow

For more information about this error, try `rustc --explain E0080`.
error: could not compile `mintypesky` (bin "mintypesky") due to 2 previous errors

It looks like the compiler evaluates the second impl with all values of N instead of realizing there is a more specific one for N=0. Also the error message is duplicated ?

It may be related to #112341. Maybe other issues but I didn't find them.
Obviously related to #76560 too

Meta

rustc --version --verbose:

rustc 1.83.0-nightly (fa724e5d8 2024-09-27)
binary: rustc
commit-hash: fa724e5d8cbbdfbd1e53c4c656121af01b694406
commit-date: 2024-09-27
host: x86_64-unknown-linux-gnu
release: 1.83.0-nightly
LLVM version: 19.1.0

I kind of expect this issue to get no answer so I might try to solve it myself. It will make for a nice project to plunge into.

@Absobel Absobel added the C-bug Category: This is a bug. label Mar 10, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Mar 10, 2025
@bjorn3
Copy link
Member

bjorn3 commented Mar 10, 2025

You got two conflicting implementations. Both implementations can be used for R1<0>. Normally you would get an error about the conflicting implementations, but in this case the const eval error likely happened while trying to determine if the second implementation matches R1<0> too.

@Absobel
Copy link
Contributor Author

Absobel commented Mar 10, 2025

But 0 doesn't respect the bound [(); N-1]: ?

This example seem to be the same thing but just with types

use std::marker::PhantomData;

struct S1<N> {
    _b: PhantomData<N>,
}
trait Bound {}
struct RespectsBound;
impl Bound for RespectsBound {}
struct DoesNot;

trait T {
    fn res(&self);
}

impl<S> T for S1<S> where S: Bound {
    fn res(&self) {
        println!("Respects bound");
    }
}
impl T for S1<DoesNot> {
    fn res(&self) {
        println!("Does not respect bound");
    }
}

fn main() {
    let respects = S1::<RespectsBound> { _b: PhantomData };
    let does_not = S1::<DoesNot> { _b: PhantomData };
    respects.res();
    does_not.res();
}
Respects bound
Does not respect bound

playground

@bjorn3
Copy link
Member

bjorn3 commented Mar 10, 2025

A trait bound not being satisfied is not an error, the trait solver will simply backtrack. I believe a const expr failing to evaluate is a hard error however without any chance of backtracking.

@Absobel
Copy link
Contributor Author

Absobel commented Mar 10, 2025

Is it a feature ? I feel like they should work the same for where clauses.

@ShE3py
Copy link
Contributor

ShE3py commented Mar 10, 2025

You probably meant to use specialization (#31844), which isn't yet implemented for associated constants. For now, you may want to use const fns in a helper struct:

struct R(usize);
impl R {
    const fn dec_or(self, default: R) -> R {
        match self.0 {
            0 => default,
            n => R(n - 1),
        }
    }
}

struct R1<const N: usize>;
const X: R1<{ R(12).dec_or(R(5)).0 }> = R1;
const Y: R1<{ R(0).dec_or(R(5)).0 }> = R1;

fn main() {
    fn print<const N: usize>(_: R1<N>) {
        println!("{N}");
    }
    
    print(X); // 11
    print(Y); // 5
}

@Absobel
Copy link
Contributor Author

Absobel commented Mar 10, 2025

Oh okay, there is a tracking issue. Thanks it's exactly that !
(Should this issue be closed then ?)

@jieyouxu jieyouxu added F-generic_const_exprs `#![feature(generic_const_exprs)]` C-discussion Category: Discussion or questions that doesn't represent real issues. and removed C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Mar 10, 2025
@jieyouxu
Copy link
Member

Closing as the discussion seems to have concluded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues. F-generic_const_exprs `#![feature(generic_const_exprs)]`
Projects
None yet
Development

No branches or pull requests

5 participants