-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Open
Labels
A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)Area: Constant evaluation, covers all const contexts (static, const fn, ...)C-discussionCategory: Discussion or questions that doesn't represent real issues.Category: Discussion or questions that doesn't represent real issues.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem teamRelevant to the opsem team
Description
I tried this code:
use std::num::NonZeroU8;
pub const UNDEFINED: Option<NonZeroU8> = Some(unsafe { NonZeroU8::new_unchecked(0) });
And assumed the compiler would reject it, but there was no compilation errors.
Note that the following also compiled:
use std::num::NonZeroU8;
pub const UNDEFINED: Option<NonZeroU8> = Some(unsafe { NonZeroU8::new_unchecked(0) });
// Sometime using the undefined constant triggers an error: not here...
pub use_undefined() -> Option<NonZeroU8> { UNDEFINED }
// This is not limited to Option !
pub enum MyOption {
Some(NonZeroU8),
None
}
pub const UNDEFINED2: MyOption = MyOption::Some(unsafe { NonZeroU8::new_unchecked(0) });
pub use_undefined2() -> MyOption { UNDEFINED2 }
I am assuming this is caused by layout optimizations ?
Meta
rustc --version --verbose
:
rustc 1.59.0 (9d1b2106e 2022-02-23)
binary: rustc
commit-hash: 9d1b2106e23b1abd32fce1f17267604a5102f57a
commit-date: 2022-02-23
host: x86_64-unknown-linux-gnu
release: 1.59.0
LLVM version: 13.0.0
rustc +nightly --version --verbose
:
rustc 1.61.0-nightly (68369a041 2022-02-22)
binary: rustc
commit-hash: 68369a041cea809a87e5bd80701da90e0e0a4799
commit-date: 2022-02-22
host: x86_64-unknown-linux-gnu
release: 1.61.0-nightly
LLVM version: 14.0.0
Metadata
Metadata
Assignees
Labels
A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)Area: Constant evaluation, covers all const contexts (static, const fn, ...)C-discussionCategory: Discussion or questions that doesn't represent real issues.Category: Discussion or questions that doesn't represent real issues.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem teamRelevant to the opsem team
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
RalfJung commentedon Mar 26, 2022
We do not guarantee that all Undefined Behavior is detected, not at run-time and not at compile-time. When you use
unsafe
, it is up to you to make sure you follow the rules.We do detect some UB, e.g. here:
But exhaustively checking for all UB is super expensive at best. Even Miri, which does a lot more work than CTFE, does not detect all UB.
So I consider this not a bug.
Cc @rust-lang/wg-const-eval
saethlin commentedon Mar 26, 2022
Miri detects this if the code is shifted to normal runtime eval. Is it worth considering moving this check into CTFE or does that just open up too many questions?
Lokathor commentedon Mar 26, 2022
on the one hand i would like CTFE to catch it, on the other I'm worried if people might think that it'll then catch the runtime version as well (which it won't).
saethlin commentedon Mar 26, 2022
I'm not so sure about that. C++ has set a precedent that const-eval has extra UB checking powers that normal runtime eval does not have.
oli-obk commentedon Mar 26, 2022
As Ralf noted, this can be very expensive. We can benchmark it to see how expensive and make a decision based on that.
We can also consider using a lint for these extra checks and only turning them on if the lint is warn/deny (and set it to allow by default for speed).
Oh, even funnier idea: clippy can overwrite the const eval query to make it run with validation. This way
cargo clippy
will catch more UB thancargo check
ChrisDenton commentedon Mar 26, 2022
Specifically in the narrow case of
NonZero*
integer types, would it be quicker for Rust to warn against any use ofnew_unchecked
while in aconst
context?saethlin commentedon Mar 26, 2022
I am suspicious of claims that this check is expensive. Miri is poorly-benchmarked, does CTFE have any benchmarks?
oli-obk commentedon Mar 26, 2022
Yes, CTFE is benchmarked with the rustc benchmarks. Trying it out is as simple as opening a PR removing
rust/compiler/rustc_const_eval/src/interpret/machine.rs
Line 424 in 8876ca3
fee1-dead commentedon Mar 27, 2022
It feels like it can just have debug_assert! inside the unchecked functions, or would it not be ideal?
saethlin commentedon Mar 27, 2022
@fee1-dead I've had a PR up for the past few months that does just that :) #92686
RalfJung commentedon Mar 27, 2022
That wouldn't make any difference here though, since the stdlib we ship is compiled without debug assertions.
saethlin commentedon Mar 27, 2022
sigh of course the bigger problem is that all those checks are disabled in const eval. And I did that because I assumed while writing it that const eval would catch all these things. Around and around we go...
@oli-obk What's wrong with just doing this?I found out what is wrong with this (MIR transforms panic if they do validation).fee1-dead commentedon Mar 27, 2022
Then an unconditional check using const_eval_select would suffice?
RalfJung commentedon Mar 27, 2022
Yes that would be an option.
10 remaining items
RalfJung commentedon Apr 3, 2022
So the check interacts badly with macros, or so? Because that should still all be unsafe code then, right?
saethlin commentedon Apr 3, 2022
I suspect the check interacts badly with functions. It's extremely local and sloppy, in the name of doing little work when there is no unsafe code. It could definitely be tightened up if you or Oli have suggestions on a more principled but 🤔 appropriately thrifty way to do this.
Whenever we call
load_mir
, the HIR for the loaded Instance is searched forunsafe
blocks. If one is found, checking is on. If one isn't found, checking is turned off.If we call
enforce_validity
without seeing a call toload_mir
and we have a stack with one frame, we check it forunsafe
. If it has nounsafe
blocks in it, checking is off. If it does contain anunsafe
block, or there is more than one stack frame, checking is on. Subsequent calls toenforce_validity
will not not change whether or not checking is turned on. Only a call toload_mir
can do that.We also defensively assume that MIR from outside the current crate contains
unsafe
.clarfonthey commentedon Jun 30, 2025
Just poking around on this: it appears that the compiler does properly check for UB in cases like this, but only if the constants are actually used. Check this example:
This will report no error, but the below will:
With the error:
It also appears to apply to underscore constants:
But it only works if the underscore constant is directly evaluated; the below still accepts the code:
saethlin commentedon Jun 30, 2025
I think you are just observing the details of what is/isn't considered a "mentioned" item, nothing to do with UB checking. We should have a meta issue somewhere for people being confused by what is and isn't considered mentioned.
RalfJung commentedon Jun 30, 2025
Indeed, the first effect you are observing is orthogonal to this issue; that is just a matter of which constants even get evaluated.
The 2nd effect is related to the issue: we only UB-check the final value of a const in detail, not all the intermediate values.