Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5c61a8d

Browse files
authoredJun 15, 2020
Rollup merge of #71824 - ecstatic-morse:const-check-post-drop-elab, r=oli-obk
Check for live drops in constants after drop elaboration Resolves #66753. This PR splits the MIR "optimization" pass series in two and introduces a query–`mir_drops_elaborated_and_const_checked`–that holds the result of the `post_borrowck_cleanup` analyses and checks for live drops. This query is invoked in `rustc_interface` for all items requiring const-checking, which means we now do `post_borrowck_cleanup` for items even if they are unused in the crate. As a result, we are now more precise about when drops are live. This is because drop elaboration can e.g. eliminate drops of a local when all its fields are moved from. This does not mean we are doing value-based analysis on move paths, however; Storing a `Some(CustomDropImpl)` into a field of a local will still set the qualifs for that entire local. r? @oli-obk
2 parents 4fb54ed + 2dcf7db commit 5c61a8d

File tree

16 files changed

+264
-51
lines changed

16 files changed

+264
-51
lines changed
 

‎src/librustc_error_codes/error_codes/E0493.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
A type with a `Drop` implementation was destructured when trying to initialize
2-
a static item.
1+
A value with a custom `Drop` implementation may be dropped during const-eval.
32

43
Erroneous code example:
54

@@ -16,13 +15,14 @@ struct Foo {
1615
field1: DropType,
1716
}
1817
19-
static FOO: Foo = Foo { ..Foo { field1: DropType::A } }; // error!
18+
static FOO: Foo = Foo { field1: (DropType::A, DropType::A).1 }; // error!
2019
```
2120

2221
The problem here is that if the given type or one of its fields implements the
23-
`Drop` trait, this `Drop` implementation cannot be called during the static
24-
type initialization which might cause a memory leak. To prevent this issue,
25-
you need to instantiate all the static type's fields by hand.
22+
`Drop` trait, this `Drop` implementation cannot be called within a const
23+
context since it may run arbitrary, non-const-checked code. To prevent this
24+
issue, ensure all values with custom a custom `Drop` implementation escape the
25+
initializer.
2626

2727
```
2828
enum DropType {

‎src/librustc_feature/active.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,9 @@ declare_features! (
577577
/// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`.
578578
(active, abi_avr_interrupt, "1.45.0", Some(69664), None),
579579

580+
/// Be more precise when looking for live drops in a const context.
581+
(active, const_precise_live_drops, "1.46.0", Some(73255), None),
582+
580583
// -------------------------------------------------------------------------
581584
// feature-group-end: actual feature gates
582585
// -------------------------------------------------------------------------

‎src/librustc_interface/passes.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,11 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
847847

848848
sess.time("MIR_effect_checking", || {
849849
for def_id in tcx.body_owners() {
850-
mir::transform::check_unsafety::check_unsafety(tcx, def_id)
850+
mir::transform::check_unsafety::check_unsafety(tcx, def_id);
851+
852+
if tcx.hir().body_const_context(def_id).is_some() {
853+
tcx.ensure().mir_drops_elaborated_and_const_checked(def_id);
854+
}
851855
}
852856
});
853857

‎src/librustc_middle/mir/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ pub enum MirPhase {
7676
Build = 0,
7777
Const = 1,
7878
Validated = 2,
79-
Optimized = 3,
79+
DropElab = 3,
80+
Optimized = 4,
8081
}
8182

8283
impl MirPhase {

‎src/librustc_middle/query/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ rustc_queries! {
190190
no_hash
191191
}
192192

193+
query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> Steal<mir::Body<'tcx>> {
194+
storage(ArenaCacheSelector<'tcx>)
195+
no_hash
196+
desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.to_def_id()) }
197+
}
198+
193199
query mir_validated(key: LocalDefId) ->
194200
(
195201
Steal<mir::Body<'tcx>>,

‎src/librustc_mir/transform/check_consts/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_middle::ty::{self, TyCtxt};
1212
pub use self::qualifs::Qualif;
1313

1414
mod ops;
15+
pub mod post_drop_elaboration;
1516
pub mod qualifs;
1617
mod resolver;
1718
pub mod validation;

‎src/librustc_mir/transform/check_consts/ops.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ use rustc_span::{Span, Symbol};
1010

1111
use super::ConstCx;
1212

13+
/// Emits an error if `op` is not allowed in the given const context.
14+
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
15+
debug!("illegal_op: op={:?}", op);
16+
17+
if op.is_allowed_in_item(ccx) {
18+
return;
19+
}
20+
21+
if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
22+
ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
23+
return;
24+
}
25+
26+
op.emit_error(ccx, span);
27+
}
28+
1329
/// An operation that is not *always* allowed in a const context.
1430
pub trait NonConstOp: std::fmt::Debug {
1531
/// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use rustc_hir::def_id::LocalDefId;
2+
use rustc_middle::mir::visit::Visitor;
3+
use rustc_middle::mir::{self, BasicBlock, Location};
4+
use rustc_middle::ty::TyCtxt;
5+
use rustc_span::Span;
6+
7+
use super::ops;
8+
use super::qualifs::{NeedsDrop, Qualif};
9+
use super::validation::Qualifs;
10+
use super::ConstCx;
11+
12+
/// Returns `true` if we should use the more precise live drop checker that runs after drop
13+
/// elaboration.
14+
pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool {
15+
tcx.features().const_precise_live_drops
16+
}
17+
18+
/// Look for live drops in a const context.
19+
///
20+
/// This is separate from the rest of the const checking logic because it must run after drop
21+
/// elaboration.
22+
pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<'tcx>) {
23+
let const_kind = tcx.hir().body_const_context(def_id);
24+
if const_kind.is_none() {
25+
return;
26+
}
27+
28+
if !checking_enabled(tcx) {
29+
return;
30+
}
31+
32+
let ccx = ConstCx {
33+
body,
34+
tcx,
35+
def_id: def_id.to_def_id(),
36+
const_kind,
37+
param_env: tcx.param_env(def_id),
38+
};
39+
40+
let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
41+
42+
visitor.visit_body(body);
43+
}
44+
45+
struct CheckLiveDrops<'mir, 'tcx> {
46+
ccx: &'mir ConstCx<'mir, 'tcx>,
47+
qualifs: Qualifs<'mir, 'tcx>,
48+
}
49+
50+
// So we can access `body` and `tcx`.
51+
impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
52+
type Target = ConstCx<'mir, 'tcx>;
53+
54+
fn deref(&self) -> &Self::Target {
55+
&self.ccx
56+
}
57+
}
58+
59+
impl CheckLiveDrops<'mir, 'tcx> {
60+
fn check_live_drop(&self, span: Span) {
61+
ops::non_const(self.ccx, ops::LiveDrop, span);
62+
}
63+
}
64+
65+
impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
66+
fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) {
67+
trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
68+
69+
// Ignore drop terminators in cleanup blocks.
70+
if block.is_cleanup {
71+
return;
72+
}
73+
74+
self.super_basic_block_data(bb, block);
75+
}
76+
77+
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
78+
trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
79+
80+
match &terminator.kind {
81+
mir::TerminatorKind::Drop { location: dropped_place, .. } => {
82+
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
83+
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
84+
return;
85+
}
86+
87+
if dropped_place.is_indirect() {
88+
self.check_live_drop(terminator.source_info.span);
89+
return;
90+
}
91+
92+
if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) {
93+
// Use the span where the dropped local was declared for the error.
94+
let span = self.body.local_decls[dropped_place.local].source_info.span;
95+
self.check_live_drop(span);
96+
}
97+
}
98+
99+
mir::TerminatorKind::DropAndReplace { .. } => span_bug!(
100+
terminator.source_info.span,
101+
"`DropAndReplace` should be removed by drop elaboration",
102+
),
103+
104+
mir::TerminatorKind::Abort
105+
| mir::TerminatorKind::Call { .. }
106+
| mir::TerminatorKind::Assert { .. }
107+
| mir::TerminatorKind::FalseEdge { .. }
108+
| mir::TerminatorKind::FalseUnwind { .. }
109+
| mir::TerminatorKind::GeneratorDrop
110+
| mir::TerminatorKind::Goto { .. }
111+
| mir::TerminatorKind::InlineAsm { .. }
112+
| mir::TerminatorKind::Resume
113+
| mir::TerminatorKind::Return
114+
| mir::TerminatorKind::SwitchInt { .. }
115+
| mir::TerminatorKind::Unreachable
116+
| mir::TerminatorKind::Yield { .. } => {}
117+
}
118+
}
119+
}

‎src/librustc_mir/transform/check_consts/validation.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub struct Qualifs<'mir, 'tcx> {
4040
}
4141

4242
impl Qualifs<'mir, 'tcx> {
43-
fn indirectly_mutable(
43+
pub fn indirectly_mutable(
4444
&mut self,
4545
ccx: &'mir ConstCx<'mir, 'tcx>,
4646
local: Local,
@@ -68,7 +68,7 @@ impl Qualifs<'mir, 'tcx> {
6868
/// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
6969
///
7070
/// Only updates the cursor if absolutely necessary
71-
fn needs_drop(
71+
pub fn needs_drop(
7272
&mut self,
7373
ccx: &'mir ConstCx<'mir, 'tcx>,
7474
local: Local,
@@ -95,7 +95,7 @@ impl Qualifs<'mir, 'tcx> {
9595
/// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
9696
///
9797
/// Only updates the cursor if absolutely necessary.
98-
fn has_mut_interior(
98+
pub fn has_mut_interior(
9999
&mut self,
100100
ccx: &'mir ConstCx<'mir, 'tcx>,
101101
local: Local,
@@ -232,30 +232,15 @@ impl Validator<'mir, 'tcx> {
232232
self.qualifs.in_return_place(self.ccx)
233233
}
234234

235-
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
236-
/// context.
237-
pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
238-
where
239-
O: NonConstOp,
240-
{
241-
debug!("check_op: op={:?}", op);
242-
243-
if op.is_allowed_in_item(self) {
244-
return;
245-
}
246-
247-
if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
248-
self.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
249-
return;
250-
}
251-
252-
op.emit_error(self, span);
253-
}
254-
255235
/// Emits an error if an expression cannot be evaluated in the current context.
256236
pub fn check_op(&mut self, op: impl NonConstOp) {
257-
let span = self.span;
258-
self.check_op_spanned(op, span)
237+
ops::non_const(self.ccx, op, self.span);
238+
}
239+
240+
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
241+
/// context.
242+
pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) {
243+
ops::non_const(self.ccx, op, span);
259244
}
260245

261246
fn check_static(&mut self, def_id: DefId, span: Span) {
@@ -577,6 +562,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
577562
// projections that cannot be `NeedsDrop`.
578563
TerminatorKind::Drop { location: dropped_place, .. }
579564
| TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
565+
// If we are checking live drops after drop-elaboration, don't emit duplicate
566+
// errors here.
567+
if super::post_drop_elaboration::checking_enabled(self.tcx) {
568+
return;
569+
}
570+
580571
let mut err_span = self.span;
581572

582573
// Check to see if the type of this place can ever have a drop impl. If not, this

‎src/librustc_mir/transform/mod.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) {
4949
mir_const,
5050
mir_const_qualif,
5151
mir_validated,
52+
mir_drops_elaborated_and_const_checked,
5253
optimized_mir,
5354
is_mir_available,
5455
promoted_mir,
@@ -294,12 +295,31 @@ fn mir_validated(
294295
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
295296
}
296297

297-
fn run_optimization_passes<'tcx>(
298+
fn mir_drops_elaborated_and_const_checked<'tcx>(
299+
tcx: TyCtxt<'tcx>,
300+
def_id: LocalDefId,
301+
) -> Steal<Body<'tcx>> {
302+
// (Mir-)Borrowck uses `mir_validated`, so we have to force it to
303+
// execute before we can steal.
304+
tcx.ensure().mir_borrowck(def_id);
305+
306+
let (body, _) = tcx.mir_validated(def_id);
307+
let mut body = body.steal();
308+
309+
run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, None);
310+
check_consts::post_drop_elaboration::check_live_drops(tcx, def_id, &body);
311+
tcx.alloc_steal_mir(body)
312+
}
313+
314+
/// After this series of passes, no lifetime analysis based on borrowing can be done.
315+
fn run_post_borrowck_cleanup_passes<'tcx>(
298316
tcx: TyCtxt<'tcx>,
299317
body: &mut Body<'tcx>,
300318
def_id: LocalDefId,
301319
promoted: Option<Promoted>,
302320
) {
321+
debug!("post_borrowck_cleanup({:?})", def_id);
322+
303323
let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
304324
// Remove all things only needed by analysis
305325
&no_landing_pads::NoLandingPads::new(tcx),
@@ -318,9 +338,24 @@ fn run_optimization_passes<'tcx>(
318338
// but before optimizations begin.
319339
&add_retag::AddRetag,
320340
&simplify::SimplifyCfg::new("elaborate-drops"),
321-
// No lifetime analysis based on borrowing can be done from here on out.
322341
];
323342

343+
run_passes(
344+
tcx,
345+
body,
346+
InstanceDef::Item(def_id.to_def_id()),
347+
promoted,
348+
MirPhase::DropElab,
349+
&[post_borrowck_cleanup],
350+
);
351+
}
352+
353+
fn run_optimization_passes<'tcx>(
354+
tcx: TyCtxt<'tcx>,
355+
body: &mut Body<'tcx>,
356+
def_id: LocalDefId,
357+
promoted: Option<Promoted>,
358+
) {
324359
let optimizations: &[&dyn MirPass<'tcx>] = &[
325360
&unreachable_prop::UnreachablePropagation,
326361
&uninhabited_enum_branching::UninhabitedEnumBranching,
@@ -368,14 +403,14 @@ fn run_optimization_passes<'tcx>(
368403

369404
let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level;
370405

406+
#[rustfmt::skip]
371407
run_passes(
372408
tcx,
373409
body,
374410
InstanceDef::Item(def_id.to_def_id()),
375411
promoted,
376412
MirPhase::Optimized,
377413
&[
378-
post_borrowck_cleanup,
379414
if mir_opt_level > 0 { optimizations } else { no_optimizations },
380415
pre_codegen_cleanup,
381416
],
@@ -393,12 +428,7 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
393428

394429
let def_id = def_id.expect_local();
395430

396-
// (Mir-)Borrowck uses `mir_validated`, so we have to force it to
397-
// execute before we can steal.
398-
tcx.ensure().mir_borrowck(def_id);
399-
400-
let (body, _) = tcx.mir_validated(def_id);
401-
let mut body = body.steal();
431+
let mut body = tcx.mir_drops_elaborated_and_const_checked(def_id).steal();
402432
run_optimization_passes(tcx, &mut body, def_id, None);
403433

404434
debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
@@ -418,6 +448,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> IndexVec<Promoted, Body<'_>>
418448
let mut promoted = promoted.steal();
419449

420450
for (p, mut body) in promoted.iter_enumerated_mut() {
451+
run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, Some(p));
421452
run_optimization_passes(tcx, &mut body, def_id, Some(p));
422453
}
423454

‎src/librustc_span/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ symbols! {
227227
const_loop,
228228
const_mut_refs,
229229
const_panic,
230+
const_precise_live_drops,
230231
const_raw_ptr_deref,
231232
const_raw_ptr_to_usize_cast,
232233
const_transmute,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0493]: destructors cannot be evaluated at compile-time
2+
--> $DIR/drop-fail.rs:10:9
3+
|
4+
LL | let x = Some(Vec::new());
5+
| ^ constants cannot evaluate destructors
6+
7+
error[E0493]: destructors cannot be evaluated at compile-time
8+
--> $DIR/drop-fail.rs:41:9
9+
|
10+
LL | let mut tmp = None;
11+
| ^^^^^^^ constants cannot evaluate destructors
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0493`.

‎src/test/ui/consts/control-flow/drop-failure.rs renamed to ‎src/test/ui/consts/control-flow/drop-fail.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
// revisions: stock precise
2+
13
#![feature(const_if_match)]
24
#![feature(const_loop)]
5+
#![cfg_attr(precise, feature(const_precise_live_drops))]
36

4-
// `x` is *not* always moved into the final value may be dropped inside the initializer.
7+
// `x` is *not* always moved into the final value and may be dropped inside the initializer.
58
const _: Option<Vec<i32>> = {
69
let y: Option<Vec<i32>> = None;
710
let x = Some(Vec::new());
8-
//~^ ERROR destructors cannot be evaluated at compile-time
11+
//[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
912

1013
if true {
1114
x
@@ -18,15 +21,15 @@ const _: Option<Vec<i32>> = {
1821
// existing analysis.
1922
const _: Vec<i32> = {
2023
let vec_tuple = (Vec::new(),);
21-
//~^ ERROR destructors cannot be evaluated at compile-time
24+
//[stock]~^ ERROR destructors cannot be evaluated at compile-time
2225

2326
vec_tuple.0
2427
};
2528

2629
// This applies to single-field enum variants as well.
2730
const _: Vec<i32> = {
2831
let x: Result<_, Vec<i32>> = Ok(Vec::new());
29-
//~^ ERROR destructors cannot be evaluated at compile-time
32+
//[stock]~^ ERROR destructors cannot be evaluated at compile-time
3033

3134
match x {
3235
Ok(x) | Err(x) => x,
@@ -36,7 +39,7 @@ const _: Vec<i32> = {
3639
const _: Option<Vec<i32>> = {
3740
let mut some = Some(Vec::new());
3841
let mut tmp = None;
39-
//~^ ERROR destructors cannot be evaluated at compile-time
42+
//[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
4043

4144
let mut i = 0;
4245
while i < 10 {

‎src/test/ui/consts/control-flow/drop-failure.stderr renamed to ‎src/test/ui/consts/control-flow/drop-fail.stock.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error[E0493]: destructors cannot be evaluated at compile-time
2-
--> $DIR/drop-failure.rs:7:9
2+
--> $DIR/drop-fail.rs:10:9
33
|
44
LL | let x = Some(Vec::new());
55
| ^ constants cannot evaluate destructors
66

77
error[E0493]: destructors cannot be evaluated at compile-time
8-
--> $DIR/drop-failure.rs:20:9
8+
--> $DIR/drop-fail.rs:23:9
99
|
1010
LL | let vec_tuple = (Vec::new(),);
1111
| ^^^^^^^^^ constants cannot evaluate destructors
1212

1313
error[E0493]: destructors cannot be evaluated at compile-time
14-
--> $DIR/drop-failure.rs:28:9
14+
--> $DIR/drop-fail.rs:31:9
1515
|
1616
LL | let x: Result<_, Vec<i32>> = Ok(Vec::new());
1717
| ^ constants cannot evaluate destructors
1818

1919
error[E0493]: destructors cannot be evaluated at compile-time
20-
--> $DIR/drop-failure.rs:38:9
20+
--> $DIR/drop-fail.rs:41:9
2121
|
2222
LL | let mut tmp = None;
2323
| ^^^^^^^ constants cannot evaluate destructors

‎src/test/ui/consts/control-flow/drop-success.rs renamed to ‎src/test/ui/consts/control-flow/drop-pass.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// run-pass
2+
// revisions: stock precise
23

34
#![feature(const_if_match)]
45
#![feature(const_loop)]
6+
#![cfg_attr(precise, feature(const_precise_live_drops))]
57

68
// `x` is always moved into the final value and is not dropped inside the initializer.
79
const _: Option<Vec<i32>> = {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-pass
2+
// gate-test-const_precise_live_drops
3+
4+
#![feature(const_if_match)]
5+
#![feature(const_loop)]
6+
#![feature(const_precise_live_drops)]
7+
8+
const _: Vec<i32> = {
9+
let vec_tuple = (Vec::new(),);
10+
vec_tuple.0
11+
};
12+
13+
const _: Vec<i32> = {
14+
let x: Result<_, Vec<i32>> = Ok(Vec::new());
15+
match x {
16+
Ok(x) | Err(x) => x,
17+
}
18+
};
19+
20+
fn main() {}

0 commit comments

Comments
 (0)
Please sign in to comment.