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
foowhere aT: NotImplementedis present which allowslolto 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