`let _ = vec![const { 1 }; 0];` failed to compile, is it a regression?

When writing this code

fn main() {
    let _ = vec![(const { 1 }); 0];
    let _ = vec![const { 1 }; 0];
}

Compiler firstly tells me line 3 is illegal (error: no rules expected the token const), then complain about the extra bracket in line 2 (warning: unnecessary parentheses around function argument), which suggests me turn line 2 into line 3.

I'm submitting issue about that, but I am not sure it is a bug or regression.

For any issue, it's a bug up until you can identify a previous version that worked, in which case it's a regression.

5 Likes

I remembered that let a = [const {1}; 0]; works, thus I suppose let a = vec![const {1}; 0]; also works.

But the latter one never works.. Thus it is a bug.

Issue is submitted.

4 Likes

It's a breaking change to parse const block as expr fragment under edition 2021 . It works under edition 2024. We should suppress the warning under edition 2021.

8 Likes

To elaborate: it has to be this way because you might have written a macro like this in the 2021 edition:

macro_rules! demo {
    ($e:expr) => {};
    (const $b:block) => {};
}

And we don't want to break that.

So we'll change the meaning of expr in the 2024 edition so that it will accept const blocks, and leave it up to the ecosystem to decide for their own macros if they want to change from expr_2021 to expr_2024.

(The standard library will probably correspondingly change the vec! macro to expr_2024 once that's possible.)

9 Likes

So it would be 100% backwards compatible to change the definition of vec to

macro_rules! vec {
    () => { ... };
    ($elem:expr_2024; $n:expr_2024) => { ... };
    ($($x:expr_2024),+ $(,)?) => { ... };
}

FYI, the same bug exists for dbg!, assert_{eq,ne}!, and matches!. It does not exist for any formatting and panicking macros, or for (debug_)assert!.

The thread_local! macro is special-cased to support inline const:

macro_rules! thread_local {
    () => { ... };
    (/*...*/ static $name:ident: $t:ty = const $init:block; $($rest:tt)*) => { ... };
    (/*...*/ static $name:ident: $t:ty = const $init:block) => { ... };
    (/*...*/ static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => { ... };
    (/*...*/ static $name:ident: $t:ty = $init:expr) => { ... };
}

This would also be a possiblity for fixing vec!, dbg!, assert_{eq,ne}!, and matches!.

3 Likes

Strictly speaking, thread_local! does more than just "support" inline const (it switches from lazy to static initialization and can be more efficient as a result). It also supported this before inline const was stable.

That's somewhat interesting. I suppose all of them just pass $($:tt)* through to format_args!, which uses the main compiler machinery for parsing, not the macro_rules! binding classes, explaining this.

1 Like