Trigger a compilation error from a macro based on const expressions

I'd like to emit a compilation error from a macro if the number of items provided exceed a given length. The problem is that the length can be a const expression so I can't check it within the macro code.

I'm wondering if there are any existing open RFCs that cover what I need.

A very simplified version of my macro, which shows the use-case:

const N: usize = 3;
let a = arr![1, 2, 3, 4; N];  // should be a compile error
let a = arr![1, 2, 3; N];  // Ok, same length
let a = arr![1, 2; N];  // Ok, fewer items than length

There are 4 values here, but the given length is 3. I want to emit a compilation error, rather than panic in this case.

There are two "obvious" ways this could work:

  1. Have a means to evaluate the 3 (which could also be any const expression) inside the macro and compare it with the number of elements. With my limited knowledge of compiler internals, implementing this in the compiler seems unlikely.
  2. Have a macro similar to compile_error, which conditionally produces a compilation error based on a const expression. e.g.
    compile_error_when!(condition, "oh no, an error");
    
    This works like compile_error except the compiler will not emit an error if the condition - which must be a const expression - resolves to false.

Has anything like this been discussed anywhere before?

1 Like

Take a look at static_assertions.

The implementation is a total hack, since the language doesn't support this natively, but it does exactly what you want.

1 Like

Thanks. It looks like I can't customise the error message with that though.

Given what you've said, this might not be of use to you, but check out proc-macro-error which leverages some of the unstable features to give better errors for nightly users, while still working on stable (with fewer features).

It doesn't give great possibilities for customizing the error, but a regular macro_rules macro can also make this check for you and emit a compiler error - most easily by doing something like let _length_check: [(); N] = [(), (), ()]; (The array literal is made by substituting each input expression with ()).

1 Like

I did something very similar to that, but I couldn't figure out a hack to only produce an error when the length is less than the number of items (as opposed to not being equal).

Hack:

trait LessThan3 {}
impl LessThan3 for [(); 0] {}
impl LessThan3 for [(); 1] {}
impl LessThan3 for [(); 2] {}

fn less_than_3<T: LessThan3>(_: T) {}

fn main() {
    less_than_3([(); 4]);
}

The problem is still that my length comes from a const expression. I can't just generate those impls because I can't evaluate the expression inside the macro.

static_assertions looks to be the best bet, but I may just resort to run-time panic. It's not ideal but at least the error messages will be understandable.