Skip to content

Commit 0f65bcd

Browse files
committed
Modify MIR building to drop foo in [foo; 0]
1 parent 222c572 commit 0f65bcd

File tree

5 files changed

+210
-5
lines changed

5 files changed

+210
-5
lines changed

compiler/rustc_mir_build/src/build/expr/as_rvalue.rs

+45-5
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5252
})
5353
}
5454
ExprKind::Repeat { value, count } => {
55-
let value_operand = unpack!(
56-
block =
57-
this.as_operand(block, scope, &this.thir[value], None, NeedsTemporary::No)
58-
);
59-
block.and(Rvalue::Repeat(value_operand, count))
55+
if Some(0) == count.try_eval_usize(this.tcx, this.param_env) {
56+
this.build_zero_repeat(block, value, scope, source_info)
57+
} else {
58+
let value_operand = unpack!(
59+
block = this.as_operand(
60+
block,
61+
scope,
62+
&this.thir[value],
63+
None,
64+
NeedsTemporary::No
65+
)
66+
);
67+
block.and(Rvalue::Repeat(value_operand, count))
68+
}
6069
}
6170
ExprKind::Binary { op, lhs, rhs } => {
6271
let lhs = unpack!(
@@ -515,6 +524,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
515524
}
516525
}
517526

527+
fn build_zero_repeat(
528+
&mut self,
529+
mut block: BasicBlock,
530+
value: ExprId,
531+
scope: Option<region::Scope>,
532+
outer_source_info: SourceInfo,
533+
) -> BlockAnd<Rvalue<'tcx>> {
534+
let this = self;
535+
let value = &this.thir[value];
536+
let elem_ty = value.ty;
537+
if let Some(Category::Constant) = Category::of(&value.kind) {
538+
// Repeating a const does nothing
539+
} else {
540+
// For a non-const, we may need to generate an appropriate `Drop`
541+
let value_operand =
542+
unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No));
543+
if let Operand::Move(to_drop) = value_operand {
544+
let success = this.cfg.start_new_block();
545+
this.cfg.terminate(
546+
block,
547+
outer_source_info,
548+
TerminatorKind::Drop { place: to_drop, target: success, unwind: None },
549+
);
550+
this.diverge_from(block);
551+
block = success;
552+
}
553+
this.record_operands_moved(&[value_operand]);
554+
}
555+
block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), Vec::new()))
556+
}
557+
518558
fn limit_capture_mutability(
519559
&mut self,
520560
upvar_span: Span,

compiler/rustc_mir_build/src/build/scope.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
10331033
self.cfg.block_data(start).terminator().kind,
10341034
TerminatorKind::Assert { .. }
10351035
| TerminatorKind::Call { .. }
1036+
| TerminatorKind::Drop { .. }
10361037
| TerminatorKind::DropAndReplace { .. }
10371038
| TerminatorKind::FalseUnwind { .. }
10381039
| TerminatorKind::InlineAsm { .. }

src/test/ui/drop/repeat-drop-2.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn borrowck_catch() {
2+
let foo = String::new();
3+
let _bar = foo;
4+
let _baz = [foo; 0]; //~ ERROR use of moved value: `foo` [E0382]
5+
}
6+
7+
const _: [String; 0] = [String::new(); 0];
8+
//~^ ERROR destructors cannot be evaluated at compile-time [E0493]
9+
10+
fn must_be_init() {
11+
let x: u8;
12+
let _ = [x; 0]; //~ ERROR: use of possibly-uninitialized variable: `x`
13+
}
14+
15+
fn main() {}

src/test/ui/drop/repeat-drop-2.stderr

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0382]: use of moved value: `foo`
2+
--> $DIR/repeat-drop-2.rs:4:17
3+
|
4+
LL | let foo = String::new();
5+
| --- move occurs because `foo` has type `String`, which does not implement the `Copy` trait
6+
LL | let _bar = foo;
7+
| --- value moved here
8+
LL | let _baz = [foo; 0];
9+
| ^^^ value used here after move
10+
11+
error[E0493]: destructors cannot be evaluated at compile-time
12+
--> $DIR/repeat-drop-2.rs:7:25
13+
|
14+
LL | const _: [String; 0] = [String::new(); 0];
15+
| -^^^^^^^^^^^^^----
16+
| ||
17+
| |constants cannot evaluate destructors
18+
| value is dropped here
19+
20+
error[E0381]: use of possibly-uninitialized variable: `x`
21+
--> $DIR/repeat-drop-2.rs:12:14
22+
|
23+
LL | let _ = [x; 0];
24+
| ^ use of possibly-uninitialized `x`
25+
26+
error: aborting due to 3 previous errors
27+
28+
Some errors have detailed explanations: E0381, E0382, E0493.
29+
For more information about an error, try `rustc --explain E0381`.

src/test/ui/drop/repeat-drop.rs

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// run-pass
2+
// ignore-wasm32-bare no unwinding panic
3+
// ignore-avr no unwinding panic
4+
// ignore-nvptx64 no unwinding panic
5+
6+
static mut CHECK: usize = 0;
7+
8+
struct DropChecker(usize);
9+
10+
impl Drop for DropChecker {
11+
fn drop(&mut self) {
12+
unsafe {
13+
if CHECK != self.0 - 1 {
14+
panic!("Found {}, should have found {}", CHECK, self.0 - 1);
15+
}
16+
CHECK = self.0;
17+
}
18+
}
19+
}
20+
21+
macro_rules! check_drops {
22+
($l:literal) => {
23+
unsafe { assert_eq!(CHECK, $l) }
24+
};
25+
}
26+
27+
struct DropPanic;
28+
29+
impl Drop for DropPanic {
30+
fn drop(&mut self) {
31+
panic!()
32+
}
33+
}
34+
35+
fn value_zero() {
36+
unsafe { CHECK = 0 };
37+
let foo = DropChecker(1);
38+
let v: [DropChecker; 0] = [foo; 0];
39+
check_drops!(1);
40+
std::mem::drop(v);
41+
check_drops!(1);
42+
}
43+
44+
fn value_one() {
45+
unsafe { CHECK = 0 };
46+
let foo = DropChecker(1);
47+
let v: [DropChecker; 1] = [foo; 1];
48+
check_drops!(0);
49+
std::mem::drop(v);
50+
check_drops!(1);
51+
}
52+
53+
const DROP_CHECKER: DropChecker = DropChecker(1);
54+
55+
fn const_zero() {
56+
unsafe { CHECK = 0 };
57+
let v: [DropChecker; 0] = [DROP_CHECKER; 0];
58+
check_drops!(0);
59+
std::mem::drop(v);
60+
check_drops!(0);
61+
}
62+
63+
fn const_one() {
64+
unsafe { CHECK = 0 };
65+
let v: [DropChecker; 1] = [DROP_CHECKER; 1];
66+
check_drops!(0);
67+
std::mem::drop(v);
68+
check_drops!(1);
69+
}
70+
71+
fn const_generic_zero<const N: usize>() {
72+
unsafe { CHECK = 0 };
73+
let v: [DropChecker; N] = [DROP_CHECKER; N];
74+
check_drops!(0);
75+
std::mem::drop(v);
76+
check_drops!(0);
77+
}
78+
79+
fn const_generic_one<const N: usize>() {
80+
unsafe { CHECK = 0 };
81+
let v: [DropChecker; N] = [DROP_CHECKER; N];
82+
check_drops!(0);
83+
std::mem::drop(v);
84+
check_drops!(1);
85+
}
86+
87+
// Make sure that things are allowed to promote as expected
88+
89+
fn allow_promote() {
90+
unsafe { CHECK = 0 };
91+
let foo = DropChecker(1);
92+
let v: &'static [DropChecker; 0] = &[foo; 0];
93+
check_drops!(1);
94+
std::mem::drop(v);
95+
check_drops!(1);
96+
}
97+
98+
// Verify that unwinding in the drop causes the right things to drop in the right order
99+
fn on_unwind() {
100+
unsafe { CHECK = 0 };
101+
std::panic::catch_unwind(|| {
102+
let panic = DropPanic;
103+
let _local = DropChecker(2);
104+
let _v = (DropChecker(1), [panic; 0]);
105+
std::process::abort();
106+
})
107+
.unwrap_err();
108+
check_drops!(2);
109+
}
110+
111+
fn main() {
112+
value_zero();
113+
value_one();
114+
const_zero();
115+
const_one();
116+
const_generic_zero::<0>();
117+
const_generic_one::<1>();
118+
allow_promote();
119+
on_unwind();
120+
}

0 commit comments

Comments
 (0)