-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add macro static_assert
to perform compile-time assertion checking
#325
Comments
I'll just note that we could add macros to the prelude on an edition boundary. Also, are there any places where macro invocations are not currently allowed, but Finally, is |
Assuming const { assert!(0 == 1) } This has the advantage that you don't need a new macro. Though the advantage of the macro is that it'll work as a top level item. |
@ChrisDenton We could, theoretically, make I'd be happy to review such a proposal if someone wanted to pitch it. It'd likely be a fairly simple lang RFC. "Allow |
Wouldn't inline-const have the same diagnostic issue as OP's |
@cuviper I think we could easily make const blocks have better error messages than |
One of the issues with the |
@joshtriplett Top-level I think the semantics could be easily defined:
is identical to
|
@chirsz-ever In a context that has const generics (e.g. a function with const generic arguments), it'd also be possible for |
I doubt that's possible. Plenty of crates already rely on the current behaviour. As one of the alternatives to this proposal, one should consider the static-assertions crate. It also provides many examples of more complex assertions (type equality/inequality, presence or absence of trait implementations, object safety). Personally I feel that simple equality & predicate assertions aren't worth adding a separate feature. They may have an issue with discoverability, but a separate static assertion feature would also require reading a specific manual, and we could just add a "static assertions" chapter to the reference/whatever other manual, with the const item trick documented. More complex assertions are a more compelling case for a separate feature. They require more complex tricks than a simple anonymous const item, and they are hard to provide good error messages for without special compiler support. |
This could "just" be a change in constant evaluation (miri) to produce a nicer error message, e.g.
instead of the current
especially since
To note, this defers the assertion from pre-monomorphization to post-monomorphization. I personally think it's important that the difference is made clear as we make post-monomorphization The difference between The fact that |
I will point out that |
This just makes me think new macros would be more confusing then helpful. Most people will end up using If people do need the pre-monomorphization version and don't know about it then is that something that diagnostics could call attention to? |
Small clarification: while C++ (dependent1) ExampleRust, passes: trait T {
fn f() {}
const STATIC_ASSERT: () = assert!(false);
}
impl<U> T for U {}
fn main() {
<i32 as T>::f();
} C++, fails: template<typename Self>
struct T
{
static void f() {}
static_assert(sizeof(Self) < 0);
// workaround MSVC missing P2593
};
int main()
{
T<int>::f();
return 0;
} I'm not certain on the specifics w.r.t. instantiation of non-template members of a templated class (and I think behavior might differ) C++ compilers don't really have an equivalent to I would personally love for constant evaluation (and thus constant evaluation errors) to happen at instantiation time instead of monomorphization time. But last I heard, the experiment with tracking required constant instantiations the way type instantiations are4 led to large (double-digit percentage, IIRC) regressions in check times5, so is unlikey to happen for check. That optimizations (dead code elimination) can impact the set of constants evaluated by In short: it's complicated. Adding a macro is a value add if (and likely only if) it can help (document and) tame the complexity somewhat. Footnotes
|
Having const asserts not happen on dead call trees (where the instantiation isn't "real" in the sense that it could be known at compile time that it'll never be called) can be desirable if the call tree itself has been eliminated by a Making const eval happen more eagerly makes asserts run for code that's intended never to be called and the resulting errors may logically be false positives. So I think pushes for more eager const eval are going to hurt some uses if no alternative to opt out is provided. |
The solution for that is to actually have " |
// the most case
const { assert!(condition) };
// out of functions
const _:() = assert!(condition); Do you know how to define a |
There is no functionality available to macros to expand to different tokens based on whether it is in item position (outside function) or expression/statement position (inside function). IIRC there was some tentative support for allowing top-level Personally, I still think there is some benefit in distinguishing between the pre-mono1 form ( Footnotes
|
Proposal
Problem statement
People often want to check some conditions at compile-time, but currently Rust does not have an intuitionistic way to do it.
Motivating examples or use cases
Here are use cases in rust-lang/rust and Rust-for-Linux:
https://github.com/rust-lang/rust/blob/432fffa8afb8fcfe658e6548e5e8f10ad2001329/library/std/src/io/error/repr_bitpacked.rs#L352
https://github.com/rust-lang/rust/blob/432fffa8afb8fcfe658e6548e5e8f10ad2001329/compiler/rustc_index/src/lib.rs#L43
https://github.com/rust-lang/rust/blob/432fffa8afb8fcfe658e6548e5e8f10ad2001329/compiler/rustc_middle/src/ty/consts/kind.rs#L75
https://github.com/Rust-for-Linux/linux/blob/cae4454fe293141be428436e5278261494cef02a/rust/kernel/static_assert.rs
People define their own versions of
static_assert
macros to meet the requirements.Solution sketch
Add a new macro
static_assert
which check it's argument in compile-time.static_assert
is allowed to used at module's top-level, not likeassert
which can only used in a function or initialization context:Like
assert
anddebug_assert
,static_assert_eq
andstatic_assert_ne
could also be added to the standard library.The error message for failed assertion would be like:
The macro can take a custom error message like
assert
, but currently we cannot call non-const formatting macro in constants. My opinion is just keeping the behavior same asassert
, in the future when formating is allowd in const context, we would automaticly get the feature.The macro should be able to be used in generic context to assert about generic parameters:
Maybe some drawbacks:
todo
,dbg
andmatches
were added to prelude, maybe this is not a big problem?const_assert
. I thinkstatic_assert
is better, becaus C, C++ and rustc developers just used it.Alternatives
There are several alternatives, the precondition is that
const_panic
is stablized since Rust 1.57, so we could perform assertion at const context, which would cause a compile-time error.We can write
const _: () = assert!(condition)
. This is stable, and works for most cases. The drawback is that it is not intuitionistic with grammar noise. People need to lean them from somewhere, such as TRPL or Rust Cookbook.Another drawback of
const _: () = assert!(condition)
is that, due to E0401, you cannot assert about generic parameters:playground
This code cannot be compiled currently, so Rust-For-Liunx has to use a
build_assert
macro.With
const
block stable since 1.79, we could writeconst { assert!(condition) }
to perform static assertion. It has no problem with generic parameters:playground
But the
const
block has another issue: it is an expression so it is not able to be written at top level. I think it is easy to define the top-levelconst
block:is identical to
Links and related work
static_assertions
crateWhat happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution:
The text was updated successfully, but these errors were encountered: