rust-lang/rust

Generic patterns and generic array lengths can call const fn with impossible generics

Opened this issue · 6 comments

// No type implements this trait
unsafe trait NotImplemented {}

const fn lol<T: NotImplemented>() -> usize {
    panic!("This should not be callable");
}

struct Dummy<T>(T);
impl<T: NotImplemented> Dummy<T> {
    const C: usize = lol::<T>();
}

fn foo<T: NotImplemented>() {
    if let Dummy::<T>::C = 1 {}
}

I expected the above code to not execute the panic, either at compile time or run time, since there is no T that implements NotImplemented. Instead, I got the following compiler error:

error[E0080]: evaluation panicked: This should not be callable
  --> src/lib.rs:10:22
   |
10 |     const C: usize = lol::<T>();
   |                      ^^^^^^^^^^ evaluation of `Dummy::<T>::C` failed inside this call
   |
note: inside `lol::<T>`
  --> src/lib.rs:5:5
   |
 5 |     panic!("This should not be callable");
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here

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

Note that the code compiles fine if I make fn lol return a value normally instead of panicking.

Also note that the code gives a reasonable compile error if I make fn lol return size_of::<T>()

Error with `size_of::()`
error[E0158]: constant pattern cannot depend on generic parameters
  --> src/lib.rs:14:12
   |
 9 | impl<T: NotImplemented> Dummy<T> {
   | --------------------------------
10 |     const C: usize = lol::<T>();
   |     -------------- constant defined here
...
13 | fn foo<T: NotImplemented>() {
   |        - constant depends on this generic parameter
14 |     if let Dummy::<T>::C = 1 {}
   |            ^^^^^^^^^^^^^ `const` depends on a generic parameter

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

Using an array length instead of a pattern also runs into a similar issue:

Code and error using array length
// No type implements this trait
unsafe trait NotImplemented {}

const fn lol<T: NotImplemented>() -> usize {
    panic!("This should not be callable");
}

struct Dummy<T>(T);
impl<T: NotImplemented> Dummy<T> {
    const C: usize = lol::<T>();
}

fn foo<T: NotImplemented>() {
    let _x = [0u8; Dummy::<T>::C];
}
error[E0080]: evaluation panicked: This should not be callable
  --> src/lib.rs:10:22
   |
10 |     const C: usize = lol::<T>();
   |                      ^^^^^^^^^^ evaluation of `Dummy::<T>::C` failed inside this call
   |
note: inside `lol::<T>`
  --> src/lib.rs:5:5
   |
 5 |     panic!("This should not be callable");
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here

note: erroneous constant encountered
  --> src/lib.rs:14:20
   |
14 |     let _x = [0u8; Dummy::<T>::C];
   |                    ^^^^^^^^^^^^^

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

Meta

Reproducible on the playground with version 1.92.0-nightly (2025-10-13 4b94758d2ba7d0ef71cc)

This can also cause strange behavior with impossible bounds:

struct Thing;
trait Trait {
    type Assoc;
}

const fn lol() where for<'a> Thing: Trait {
    let _x = None::<<Thing as Trait>::Assoc>;
}

struct Dummy;
impl Dummy where for<'a> Thing: Trait {
    const C: () = lol();
}

fn foo() where for<'a> Thing: Trait {
    if let Dummy::C = () {}
}
error[E0080]: entering unreachable code
  --> src/lib.rs:12:19
   |
12 |     const C: () = lol();
   |                   ^^^^^ evaluation of `Dummy::C` failed here

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

So apparently what happens is that

  • the check that should prevent patterns from containing generics doesn't work
  • we end up running const-eval on the generic const Dummy::<T>::C
  • that evaluation happens in the typing_env of foo where a T: NotImplemented is present which allows lol to be called

Maybe pattern compilation should evaluate constants in a different typing_env where the surrounding generics are not available, since they should not be used... but if the generic const check would work correctly, that'd never make a difference.

this was something I was kind of working on at some point but wound up down a rabbithole 🤔 I think probably we want the "const patterns cant depend on generics" check to be syntactic (i.e. how it works for const generics) rather than based on CTFE returning TooGeneric, and then CTFE just only evaluates stuff that doesnt name any generic parameters

This is almost certainly a breaking change and I'm not sure exactly how breaking it would be. Array lengths will be solved at some point when const_evaluatable_unchecked becomes a hard error- that code is FCW'd already.

There are probably follow on things we'd need to do after this to make all cases work properly:tm: but I think not running const eval on generic code is likely to be a necessary first step