Skip to content

Commit ed58a2b

Browse files
committed
Auto merge of #79100 - a1phyr:better_assert_eq, r=m-ou-se
Improve assert_eq! and assert_ne! This PR improves `assert_eq!` and `assert_ne!` by moving the panicking code in an external function. It does not change the fast path, but the move of the formatting in the cold path (the panic) may have a positive effect on in instruction cache use and with inlining. Moreover, the use of trait objects instead of generic may improve compile times for `assert_eq!`-heavy code. Godbolt link: ~~https://rust.godbolt.org/z/TYa9MT~~ \ Updated: https://rust.godbolt.org/z/bzE84x
2 parents a31c162 + 7333759 commit ed58a2b

15 files changed

+292
-831
lines changed

library/core/src/macros/mod.rs

+11-15
Original file line numberDiff line numberDiff line change
@@ -53,32 +53,30 @@ macro_rules! panic {
5353
/// ```
5454
#[macro_export]
5555
#[stable(feature = "rust1", since = "1.0.0")]
56+
#[allow_internal_unstable(core_panic)]
5657
macro_rules! assert_eq {
5758
($left:expr, $right:expr $(,)?) => ({
5859
match (&$left, &$right) {
5960
(left_val, right_val) => {
6061
if !(*left_val == *right_val) {
62+
let kind = $crate::panicking::AssertKind::Eq;
6163
// The reborrows below are intentional. Without them, the stack slot for the
6264
// borrow is initialized even before the values are compared, leading to a
6365
// noticeable slow down.
64-
$crate::panic!(r#"assertion failed: `(left == right)`
65-
left: `{:?}`,
66-
right: `{:?}`"#, &*left_val, &*right_val)
66+
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::None);
6767
}
6868
}
6969
}
7070
});
7171
($left:expr, $right:expr, $($arg:tt)+) => ({
72-
match (&($left), &($right)) {
72+
match (&$left, &$right) {
7373
(left_val, right_val) => {
7474
if !(*left_val == *right_val) {
75+
let kind = $crate::panicking::AssertKind::Eq;
7576
// The reborrows below are intentional. Without them, the stack slot for the
7677
// borrow is initialized even before the values are compared, leading to a
7778
// noticeable slow down.
78-
$crate::panic!(r#"assertion failed: `(left == right)`
79-
left: `{:?}`,
80-
right: `{:?}`: {}"#, &*left_val, &*right_val,
81-
$crate::format_args!($($arg)+))
79+
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::format_args!($($arg)+)));
8280
}
8381
}
8482
}
@@ -104,17 +102,17 @@ macro_rules! assert_eq {
104102
/// ```
105103
#[macro_export]
106104
#[stable(feature = "assert_ne", since = "1.13.0")]
105+
#[allow_internal_unstable(core_panic)]
107106
macro_rules! assert_ne {
108107
($left:expr, $right:expr $(,)?) => ({
109108
match (&$left, &$right) {
110109
(left_val, right_val) => {
111110
if *left_val == *right_val {
111+
let kind = $crate::panicking::AssertKind::Ne;
112112
// The reborrows below are intentional. Without them, the stack slot for the
113113
// borrow is initialized even before the values are compared, leading to a
114114
// noticeable slow down.
115-
$crate::panic!(r#"assertion failed: `(left != right)`
116-
left: `{:?}`,
117-
right: `{:?}`"#, &*left_val, &*right_val)
115+
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::None);
118116
}
119117
}
120118
}
@@ -123,13 +121,11 @@ macro_rules! assert_ne {
123121
match (&($left), &($right)) {
124122
(left_val, right_val) => {
125123
if *left_val == *right_val {
124+
let kind = $crate::panicking::AssertKind::Ne;
126125
// The reborrows below are intentional. Without them, the stack slot for the
127126
// borrow is initialized even before the values are compared, leading to a
128127
// noticeable slow down.
129-
$crate::panic!(r#"assertion failed: `(left != right)`
130-
left: `{:?}`,
131-
right: `{:?}`: {}"#, &*left_val, &*right_val,
132-
$crate::format_args!($($arg)+))
128+
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::format_args!($($arg)+)));
133129
}
134130
}
135131
}

library/core/src/panicking.rs

+51
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,54 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
9191
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
9292
unsafe { panic_impl(&pi) }
9393
}
94+
95+
#[derive(Debug)]
96+
#[doc(hidden)]
97+
pub enum AssertKind {
98+
Eq,
99+
Ne,
100+
}
101+
102+
/// Internal function for `assert_eq!` and `assert_ne!` macros
103+
#[cold]
104+
#[track_caller]
105+
#[doc(hidden)]
106+
pub fn assert_failed<T, U>(
107+
kind: AssertKind,
108+
left: &T,
109+
right: &U,
110+
args: Option<fmt::Arguments<'_>>,
111+
) -> !
112+
where
113+
T: fmt::Debug + ?Sized,
114+
U: fmt::Debug + ?Sized,
115+
{
116+
#[track_caller]
117+
fn inner(
118+
kind: AssertKind,
119+
left: &dyn fmt::Debug,
120+
right: &dyn fmt::Debug,
121+
args: Option<fmt::Arguments<'_>>,
122+
) -> ! {
123+
let op = match kind {
124+
AssertKind::Eq => "==",
125+
AssertKind::Ne => "!=",
126+
};
127+
128+
match args {
129+
Some(args) => panic!(
130+
r#"assertion failed: `(left {} right)`
131+
left: `{:?}`,
132+
right: `{:?}: {}`"#,
133+
op, left, right, args
134+
),
135+
None => panic!(
136+
r#"assertion failed: `(left {} right)`
137+
left: `{:?}`,
138+
right: `{:?}`"#,
139+
op, left, right,
140+
),
141+
}
142+
}
143+
inner(kind, &left, &right, args)
144+
}

0 commit comments

Comments
 (0)