Implement RFC-2011 (Nicer assert! messages) by c410-f3r · Pull Request #96496 · rust-lang/rust (original) (raw)

Based on #48973
CC #44838

Functionality

The intention here is to show the contents of variables that are outside the declaration of assert!( ... ).

Current behavior

fn main() { let a = 1; let b = true; assert!(a == 1 && !b); }

// Output: // // assertion failed: a == 1 && !b

New behavior with this PR

#![feature(generic_assert)]

fn main() { let a = 1; let b = true; assert!(a == 1 && !b); }

// Output: // // Assertion failed: a == 1 && !b // With captures: // a = 1 // b = true

Internals

All external assert! variables are replaced by a block that tries to copy the intended value and then use this possibly copied element to print useful messages if a branch occurs.

fn main() { #[derive(PartialEq)] struct NoDebug(i32);

let no_debug = NoDebug(1);
let number = 3i32;

assert!(no_debug != NoDebug(2) && 1 == number);

}

With this reasoning in mind, the above assert! will more or less expand to the following code:

#[derive(PartialEq)] struct NoDebug(i32);

let no_debug = NoDebug(1); let number = 3i32;

{ let mut __capture0 = Capture::new(); let mut __capture1 = Capture::new();

let __local_bind0 = &no_debug;
let __local_bind1 = &number;

if !(
    // no_debug != NoDebug(2)
    *{
        (&Wrapper(__local_bind0)).try_capture(&mut __capture1);
        __local_bind0
    } != NoDebug(2)
    // 1 == number
    && 1 == *{
        (&Wrapper(__local_bind1)).try_capture(&mut __capture2);
        __local_bind1
    }
) {
    panic!(
        "Assertion failed: no_debug != NoDebug(2) && 1 == number\nWith captures:\n  no_debug = {:?}\n  number = {:?}",
        __capture0,
        __capture1,
    );
}

}

The compiler "side" outputs the necessary machinery to make the library "side" decide how an element T will or will not be printed.

Performance

It is inevitable that more instructions will be issued but probably nothing that will have a huge negative impact. Nevertheless, it is possible to optimize certain scenarios to reduce codegen.

; https://godbolt.org/z/94rE4dE4a (worst-case) example::with_generic_assert: sub rsp, 104 mov dword ptr [rsp + 16], 0 mov dword ptr [rsp + 8], 0 cmp edi, 2 je .LBB2_3 mov dword ptr [rsp + 8], 1 mov dword ptr [rsp + 12], esi cmp esi, 1 jne .LBB2_3 add rsp, 104 ret

; https://godbolt.org/z/P8T4rboac (best-case) example::with_generic_assert: sub rsp, 104 mov dword ptr [rsp + 16], 0 cmp edi, 2 je .LBB2_3 cmp esi, 1 jne .LBB2_3 add rsp, 104 ret

example::without_generic_assert: cmp edi, 2 je .LBB3_3 cmp esi, 1 jne .LBB3_3 ret