Idea: make assert!() a keyword - REJECTED

I think keyword is probably overkill, but attribute might be interesting.

There's two things I'm thinking about there:

  1. If it's an attribute, lints could read it to provide generic lints about constant expressions passed to the function.

For example, clippy has a specific iterator_step_by_zero lint. But step_by has an assert!(step != 0)†, so if that were exposed to the lint infrastructure it could be one lint for "always asserts".

And who knows, maybe that would also make it easier for a more complicated static analysis tool to try to prove/disprove them as well, for more than just constants.

† Though not in exactly the place we'd want for what I'm talking about here, but that's a side point and it could be moved.

  1. If it's an attribute, it could also do more complicated transformations to make it more likely that the asserts will be checked in the caller.

This could do something along the lines of outlining, for more complicated methods. Imaging a complicated, non-generic function:

pub fn foo(x: i32) -> i32 { 
    assert_eq!(x & 1, 0);
    ... lots of complicated work here ...
}

We want to compile the complicated stuff only once, but we'd also like to elide the asserts by evaluating them in the caller. So imagine that we transformed that code into something like this:

#[inline]
pub fn foo(x: usize) -> String { 
    assert!(x < isize::MAX as usize);
    unsafe { inner(x) }

    #[forbid(unsafe-op-in-unsafe-fn)]
    unsafe fn inner(x: i32) -> i32 {
        unsafe { assume(x < isize::MAX as usize) };
        ... lots of complicated work here ...
    }
}

That way the conditions can get inlined and hopefully folded away, while still allowing separate compilation of the complicated bit and letting LLVM to optimize based on the conditions.

(There some clear weaknesses in here, like assume often being not a good idea, but hopefully it gets the idea across.)

1 Like