Open
Description
I tried this code:
#![feature(box_syntax)]
fn main() {
let _x = box return;
}
miri reports UB on this code:
rning: unreachable expression
--> src/main.rs:4:14
|
4 | let _x = box return;
| ^^^^------
| | |
| | any code following this expression is unreachable
| unreachable expression
|
= note: `#[warn(unreachable_code)]` on by default
error: Undefined Behavior: type validation failed: encountered a box pointing to uninhabited type !
--> src/main.rs:4:14
|
4 | let _x = box return;
| ^^^^^^^^^^ type validation failed: encountered a box pointing to uninhabited type !
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: inside `main` at src/main.rs:4:14
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error; 1 warning emitted
Meta
rustc --version --verbose
:
rustc 1.63.0-nightly (9257f5aad 2022-05-21)
binary: rustc
commit-hash: 9257f5aad02b65665a6e23e5b92938548302e129
commit-date: 2022-05-21
host: x86_64-unknown-linux-gnu
release: 1.63.0-nightly
LLVM version: 14.0.4
Backtrace
error: Undefined Behavior: type validation failed: encountered a box pointing to uninhabited type !
--> src/main.rs:4:14
|
4 | let _x = box return;
| ^^^^^^^^^^ type validation failed: encountered a box pointing to uninhabited type !
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: inside `main` at src/main.rs:4:14
= note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:248:5
= note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:122:18
= note: inside closure at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:145:18
= note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:280:13
= note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:492:40
= note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:456:19
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:137:14
= note: inside closure at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:48
= note: inside `std::panicking::r#try::do_call::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:492:40
= note: inside `std::panicking::r#try::<isize, [closure@std::rt::lang_start_internal::{closure#2}]>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:456:19
= note: inside `std::panic::catch_unwind::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:137:14
= note: inside `std::rt::lang_start_internal` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:20
= note: inside `std::rt::lang_start::<()>` at /home/matthias/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:144:17
error: aborting due to previous error; 1 warning emitted
/tmp/f on ma
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
RalfJung commentedon Jun 3, 2022
Ah fun times, we are actually generating a
Box<!>
here?Maybe
Box
should just be less special in terms of validity etc than it is now...RalfJung commentedon Aug 18, 2024
This is the same problem that also led me to exclude
Box
in #128531:box foo
compiles toHowever, the uninitialized box already has type
Box<T>
, and that's fundamentally incompatible with any kind of recursive validity checking.[-]miri: box_syntax: type validation failed: encountered a box pointing to uninhabited type ![/-][+]box syntax creates Box<T> where T is not yet initialized[/+]RalfJung commentedon Aug 18, 2024
@rust-lang/wg-mir-opt (is that the right ping group for MIR building questions?)
Currently, this
creates MIR roughly like this
The problem with this is that we call
ShallowInitBox
before the box has been initialized. Unsurprisingly, if we have Miri ensure thatBox<T>
actually refers to an initializedT
, this explodes.Is it possible for MIR generation to only call
ShallowInitBox
when the box contents are actually initialized, or alternatively forShallowInitBox
to not "lie" in its return type where it currently claims that we have aBox<T>
?This has probably been a problem since #89030, maybe longer than that.
Cc @nbdd0121
nbdd0121 commentedon Aug 18, 2024
This issue is certainly not introduced by
ShallowInitBox
. BeforeShallowInitBox
, box creation happens by a NullaryOp which just gives an uninitialized box, andShallowInitBox
changes the process to be alloc and then turn the pointer to a shallow inited box.We marked the rvalue returned by
ShallowInitBox
as shallowly initialized so the box is valid but not its contents. This mechanism is introduced in #43772RalfJung commentedon Aug 18, 2024
That's an initialization tracker / drop elaboration thing, though, if I understand correctly. It is not reflected in the type and therefore the operational semantics of the program.
RalfJung commentedon Aug 18, 2024
I guess another possible fix would be to say that
ShallowInitBox
does an untyped copy of the argument into the destination place. But that's inconsistent with how all the otherRvalue
work -- they generally compute values, hence their name, and values are typed. So if we go that route we should represent this differently, e.g. as a statement or aNonDivergingIntrinsic
.nbdd0121 commentedon Aug 18, 2024
The shallow initialized box is not very different from a Box where its content have been moved. I guess the issue here is with uninhabited types there's no notion of a moved-out box of that type?
Also, could you explain why a value of
Box<!>
could not exist? Why couldn't we treat it similar to other custom smart pointers where their validity is shallow, so their mere existence is fine if they are not dereferenced?RalfJung commentedon Aug 18, 2024
The problem is that operationally speaking, we are moving the shallow initialized box:
_0 = ShallowInitBox(move _4, T);
moves it from_4
to_0
. We never allow moving boxes whose contents have been moved out so there it's not a problem.See rust-lang/unsafe-code-guidelines#413 (and note that
Box
is a primitive pointer type and thus a lot like a reference; it's not just a library smart pointer).nbdd0121 commentedon Aug 18, 2024
I see. In this case I think making this a statement / non-diverging intrinsic probably makes sense.
est31 commentedon Aug 22, 2024
What about having
ShallowInitBox
returning a type that can actually point to uninitialized memory? SayNonNull<T>
?NonNull<!>
is allowed, right? I'd like to avoid moving around the content pointed to, say from aMaybeUninit<T>
to aBox<T>
: ideally we just cast pointers.nbdd0121 commentedon Aug 22, 2024
The point of
ShallowInitBox
is to deallocate the box when the boxed expression fails, so it should return a box type.We could codegen as allocating a
Box<MaybeUninit<T>>
and then derive a pointer from it, cast to*mut T
, use it's deref as place for the boxed expression, thenassume_init
, although we would be generating much more MIR in that case and I don't know what the perf impact would be like.10 remaining items