Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 21bdfa1

Browse files
committed
Ensure consistent drop for hint::select_unpredictable
There are a few alternatives to the implementation. The principal problem is that the selected value must be owned (in the sense of having any drop flag of sorts) when the unselected value is dropped, such that panic unwind goes through the drop of both. This ownership must then be passed on in return when the drop went smoothly. The basic way of achieving this is by extracting the selected value first, at the cost of relying on the optimizer a little more for detecting the copy as constructing the return value despite having a place in the body.
1 parent ca77504 commit 21bdfa1

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

‎library/core/src/hint.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -790,8 +790,12 @@ pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T
790790
// is returned. This is necessary because the intrinsic doesn't drop the
791791
// value that is not selected.
792792
unsafe {
793+
// Extract the selected value first, ensure it is dropped as well if dropping the unselected
794+
// value panics.
795+
let ret = crate::intrinsics::select_unpredictable(condition, &true_val, &false_val)
796+
.assume_init_read();
793797
crate::intrinsics::select_unpredictable(!condition, &mut true_val, &mut false_val)
794798
.assume_init_drop();
795-
crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init()
799+
ret
796800
}
797801
}

‎library/coretests/tests/hint.rs‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,40 @@ fn select_unpredictable_drop() {
2121
assert!(a_dropped.get());
2222
assert!(b_dropped.get());
2323
}
24+
25+
#[test]
26+
#[should_panic]
27+
fn select_unpredictable_drop_on_panic() {
28+
use core::cell::Cell;
29+
30+
struct X<'a> {
31+
cell: &'a Cell<u16>,
32+
expect: u16,
33+
write: u16,
34+
}
35+
36+
impl Drop for X<'_> {
37+
fn drop(&mut self) {
38+
let value = self.cell.get();
39+
self.cell.set(self.write);
40+
assert_eq!(value, self.expect);
41+
}
42+
}
43+
44+
let cell = Cell::new(0);
45+
46+
// Trigger a double-panic if the selected cell was not dropped during panic.
47+
let _armed = X { cell: &cell, expect: 0xdead, write: 0 };
48+
let selected = X { cell: &cell, write: 0xdead, expect: 1 };
49+
let unselected = X { cell: &cell, write: 1, expect: 0xff };
50+
51+
// The correct drop order is:
52+
//
53+
// 1. `unselected` drops, writes 1, and panics as 0 != 0xff
54+
// 2. `selected` drops during unwind, writes 0xdead and does not panic as 1 == 1
55+
// 3. `armed` drops during unwind, writes 0 and does not panic as 0xdead == 0xdead
56+
//
57+
// If `selected` is not dropped, `armed` panics as 1 != 0xdead
58+
let _unreachable =
59+
core::hint::select_unpredictable(core::hint::black_box(true), selected, unselected);
60+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /