Description
Consider the following code:
pub struct G;
pub trait IAmArray{
const SIZE: usize;
}
impl<T, const N: usize> IAmArray for [T; N]{
const SIZE: usize = N;
}
impl G{
pub fn i_am_break_on_different_array_sizes<A, B>(_a: A, _b: B)
where A: IAmArray,
B: IAmArray,
{
trait CompileAssert{
const TRIGGER: ();
}
impl<A, B> CompileAssert for (A, B)
where A: IAmArray,
B: IAmArray,
{
const TRIGGER: () = if A::SIZE == B::SIZE {} else {panic!("You must provide arrays of same length")};
}
let _ = <(A, B) as CompileAssert>::TRIGGER;
}
}
fn main() {
G::i_am_break_on_different_array_sizes([0u8;5], [0u32;3]);
}
When I do a check-build of this (rustc --emit=metadata
), it works fine. But when I do a full build (rustc --emit=link
), it fails:
error[E0080]: evaluation of `<([u8; 5], [u32; 3]) as G::i_am_break_on_different_array_sizes::CompileAssert>::TRIGGER` failed
--> test.rs:23:64
|
23 | const TRIGGER: () = if A::SIZE == B::SIZE {} else {panic!("You must provide arrays of same length")};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'You must provide arrays of same length', test.rs:23:64
|
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: the above error was encountered while instantiating `fn G::i_am_break_on_different_array_sizes::<[u8; 5], [u32; 3]>`
--> test.rs:31:5
|
31 | G::i_am_break_on_different_array_sizes([0u8;5], [0u32;3]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
This is a post-monomorphization error. It is a long-known problem but I could not find an issue tracking it.
It seems worth tracking as it is rather surprising, so if we can find a way to fix it, that would be great. It also leads to issues with Miri such as rust-lang/miri#2423.
All that said, currently this is intended behavior. This RFC gives some relevant background:
cargo check should catch as many errors as possible, but the emphasis of cargo check is on giving a "fast" answer rather than giving a "complete" answer. If you need a complete answer with all possible errors accounted for then you must use cargo build. The rationale for this is that giving a "complete" answer requires (among other things) doing full monomorphization (since some errors, such as those related to associated consts, can only be caught during monomorphization). Monomorphization is expensive: instead of having to check each function only once, each function now has to be checked once for all choices of generic parameters that the crate needs. Given this performance cost and the fact that errors during monomorphization are fairly rare, cargo check favors speed over completeness.
This is related to #49292 but not the same -- that issue is about lints that arise pre-monomorphization, during MIR passes that are skipped in check builds. This here is about errors that appear even later during compilation.