Description
Not sure if this should be considered a bug or a diagnostic issue.
Having a const left_val
or const right_val
declared breaks assert_eq!
. This has to do with its expansion and Rust's rules for macro hygiene: https://sabrinajewson.org/blog/truly-hygienic-let
Consider this code
fn main() {
let x: u8 = 0;
assert_eq!(x, 0);
}
according to cargo expand
it expands to
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
let x: u8 = 0;
match (&x, &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(
kind,
&*left_val,
&*right_val,
::core::option::Option::None,
);
}
}
};
}
Since assert_eq!
wants to use the value of the provided expressions twice (once for comparison, once for printing the result on failure), but it only wants to evaluate each expression once, it does a match
to bind them to a pattern (left_val, right_val)
. However, having a const
named left_val
or right_val
in scope changes the meaning of the pattern.
fn main() {
let x: u8 = 0;
const left_val: i8 = -123;
assert_eq!(x, 0);
}
error[E0308]: mismatched types
--> src/main.rs:4:5
|
3 | const left_val: i8 = -123;
| ------------------ constant defined here
4 | assert_eq!(x, 0);
| ^^^^^^^^^^^^^^^^
| |
| expected `&u8`, found `i8`
| this expression has type `(&u8, &{integer})`
| `left_val` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_left_val`
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0614]: type `i8` cannot be dereferenced
--> src/main.rs:4:5
|
4 | assert_eq!(x, 0);
| ^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
The error message, admittedly, is not very helpful.
Thankfully, you can't use this to make assert_eq
pass/fail when it shouldn't. The worst you can achieve is a cryptic error message from the compiler. I think. So this "bug" is not really exploitable, plus chances of accidentally breaking this are probably pretty low (const
s are usually named in UPPER_CASE
in Rust), but the diagnostic is admittedly not very helpful.
The article I've linked above (https://sabrinajewson.org/blog/truly-hygienic-let) offers a potential solution for this. TL;DR: due to shadowing shenanigans having a function named left_val
will prevent left_val
from being interpreted as a const in patterns.
@rustbot label A-macros A-diagnostics C-bug D-confusing D-terse