Skip to content

Commit 6ab6f74

Browse files
Gate const drop behind const_destruct feature, and fix const_precise_live_drops post-drop-elaboration check
1 parent 300d9c5 commit 6ab6f74

27 files changed

+338
-102
lines changed

compiler/rustc_const_eval/src/check_consts/check.rs

+26-18
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_span::{Span, Symbol, sym};
2323
use rustc_trait_selection::traits::{
2424
Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
2525
};
26-
use tracing::{debug, instrument, trace};
26+
use tracing::{instrument, trace};
2727

2828
use super::ops::{self, NonConstOp, Status};
2929
use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
@@ -46,7 +46,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
4646
/// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
4747
///
4848
/// Only updates the cursor if absolutely necessary
49-
fn needs_drop(
49+
pub(crate) fn needs_drop(
5050
&mut self,
5151
ccx: &'mir ConstCx<'mir, 'tcx>,
5252
local: Local,
@@ -314,6 +314,14 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
314314
}
315315
}
316316

317+
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
318+
/// context. This is meant for use in a post-const-checker pass such as the const precise
319+
/// // live drops lint.
320+
pub fn check_op_spanned_post<O: NonConstOp<'tcx>>(mut self, op: O, span: Span) {
321+
self.check_op_spanned(op, span);
322+
assert!(self.secondary_errors.is_empty());
323+
}
324+
317325
fn check_static(&mut self, def_id: DefId, span: Span) {
318326
if self.tcx.is_thread_local_static(def_id) {
319327
self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
@@ -839,12 +847,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
839847
let mut err_span = self.span;
840848
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
841849

842-
let ty_needs_non_const_drop =
843-
qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
844-
845-
debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);
846-
847-
if !ty_needs_non_const_drop {
850+
let needs_drop = if let Some(local) = dropped_place.as_local() {
851+
self.qualifs.needs_drop(self.ccx, local, location)
852+
} else {
853+
qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
854+
};
855+
// If this type doesn't need a drop at all, then there's nothing to enforce.
856+
if !needs_drop {
848857
return;
849858
}
850859

@@ -853,18 +862,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
853862
err_span = self.body.local_decls[local].source_info.span;
854863
self.qualifs.needs_non_const_drop(self.ccx, local, location)
855864
} else {
856-
true
865+
qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
857866
};
858867

859-
if needs_non_const_drop {
860-
self.check_op_spanned(
861-
ops::LiveDrop {
862-
dropped_at: Some(terminator.source_info.span),
863-
dropped_ty: ty_of_dropped_place,
864-
},
865-
err_span,
866-
);
867-
}
868+
self.check_op_spanned(
869+
ops::LiveDrop {
870+
dropped_at: Some(terminator.source_info.span),
871+
dropped_ty: ty_of_dropped_place,
872+
needs_non_const_drop,
873+
},
874+
err_span,
875+
);
868876
}
869877

870878
TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),

compiler/rustc_const_eval/src/check_consts/ops.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -452,15 +452,40 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm {
452452
pub(crate) struct LiveDrop<'tcx> {
453453
pub dropped_at: Option<Span>,
454454
pub dropped_ty: Ty<'tcx>,
455+
pub needs_non_const_drop: bool,
455456
}
456457
impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
458+
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
459+
if self.needs_non_const_drop {
460+
Status::Forbidden
461+
} else {
462+
Status::Unstable {
463+
gate: sym::const_destruct,
464+
safe_to_expose_on_stable: false,
465+
is_function_call: false,
466+
}
467+
}
468+
}
469+
457470
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
458-
ccx.dcx().create_err(errors::LiveDrop {
459-
span,
460-
dropped_ty: self.dropped_ty,
461-
kind: ccx.const_kind(),
462-
dropped_at: self.dropped_at,
463-
})
471+
if self.needs_non_const_drop {
472+
ccx.dcx().create_err(errors::LiveDrop {
473+
span,
474+
dropped_ty: self.dropped_ty,
475+
kind: ccx.const_kind(),
476+
dropped_at: self.dropped_at,
477+
})
478+
} else {
479+
ccx.tcx.sess.create_feature_err(
480+
errors::LiveDrop {
481+
span,
482+
dropped_ty: self.dropped_ty,
483+
kind: ccx.const_kind(),
484+
dropped_at: self.dropped_at,
485+
},
486+
sym::const_destruct,
487+
)
488+
}
464489
}
465490
}
466491

compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs

+34-28
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use rustc_middle::mir::visit::Visitor;
22
use rustc_middle::mir::{self, BasicBlock, Location};
3-
use rustc_middle::ty::{Ty, TyCtxt};
4-
use rustc_span::Span;
3+
use rustc_middle::ty::TyCtxt;
54
use rustc_span::symbol::sym;
65
use tracing::trace;
76

87
use super::ConstCx;
98
use super::check::Qualifs;
10-
use super::ops::{self, NonConstOp};
9+
use super::ops::{self};
1110
use super::qualifs::{NeedsNonConstDrop, Qualif};
11+
use crate::check_consts::check::Checker;
12+
use crate::check_consts::qualifs::NeedsDrop;
1213
use crate::check_consts::rustc_allow_const_fn_unstable;
1314

1415
/// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -66,12 +67,6 @@ impl<'mir, 'tcx> std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
6667
}
6768
}
6869

69-
impl<'tcx> CheckLiveDrops<'_, 'tcx> {
70-
fn check_live_drop(&self, span: Span, dropped_ty: Ty<'tcx>) {
71-
ops::LiveDrop { dropped_at: None, dropped_ty }.build_error(self.ccx, span).emit();
72-
}
73-
}
74-
7570
impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
7671
fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) {
7772
trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
@@ -89,28 +84,39 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
8984

9085
match &terminator.kind {
9186
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
92-
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
93-
94-
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
95-
// Instead of throwing a bug, we just return here. This is because we have to
96-
// run custom `const Drop` impls.
87+
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
88+
89+
let needs_drop = if let Some(local) = dropped_place.as_local() {
90+
self.qualifs.needs_drop(self.ccx, local, location)
91+
} else {
92+
NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
93+
};
94+
// If this type doesn't need a drop at all, then there's nothing to enforce.
95+
if !needs_drop {
9796
return;
9897
}
9998

100-
if dropped_place.is_indirect() {
101-
self.check_live_drop(terminator.source_info.span, dropped_ty);
102-
return;
103-
}
104-
105-
// Drop elaboration is not precise enough to accept code like
106-
// `tests/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is
107-
// initialized with `None` and never changed, it still emits drop glue.
108-
// Hence we additionally check the qualifs here to allow more code to pass.
109-
if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) {
110-
// Use the span where the dropped local was declared for the error.
111-
let span = self.body.local_decls[dropped_place.local].source_info.span;
112-
self.check_live_drop(span, dropped_ty);
113-
}
99+
let mut err_span = terminator.source_info.span;
100+
101+
let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
102+
// Use the span where the local was declared as the span of the drop error.
103+
err_span = self.body.local_decls[local].source_info.span;
104+
self.qualifs.needs_non_const_drop(self.ccx, local, location)
105+
} else {
106+
NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
107+
};
108+
109+
// I know it's not great to be creating a new const checker, but I'd
110+
// rather use it so we can deduplicate the error emitting logic that
111+
// it contains.
112+
Checker::new(self.ccx).check_op_spanned_post(
113+
ops::LiveDrop {
114+
dropped_at: Some(terminator.source_info.span),
115+
dropped_ty: ty_of_dropped_place,
116+
needs_non_const_drop,
117+
},
118+
err_span,
119+
);
114120
}
115121

116122
mir::TerminatorKind::UnwindTerminate(_)

compiler/rustc_const_eval/src/check_consts/qualifs.rs

+17-19
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ pub struct NeedsDrop;
136136
impl Qualif for NeedsDrop {
137137
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
138138
const IS_CLEARED_ON_MOVE: bool = true;
139+
const ALLOW_PROMOTED: bool = true;
139140

140141
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
141142
qualifs.needs_drop
@@ -175,25 +176,22 @@ impl Qualif for NeedsNonConstDrop {
175176
// that the components of this type are also `~const Destruct`. This
176177
// amounts to verifying that there are no values in this ADT that may have
177178
// a non-const drop.
178-
if cx.tcx.features().const_trait_impl() {
179-
let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
180-
let infcx = cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.param_env));
181-
let ocx = ObligationCtxt::new(&infcx);
182-
ocx.register_obligation(Obligation::new(
183-
cx.tcx,
184-
ObligationCause::misc(cx.body.span, cx.def_id()),
185-
cx.param_env,
186-
ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
187-
.to_host_effect_clause(cx.tcx, match cx.const_kind() {
188-
rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
189-
rustc_hir::ConstContext::Static(_)
190-
| rustc_hir::ConstContext::Const { .. } => ty::BoundConstness::Const,
191-
}),
192-
));
193-
!ocx.select_all_or_error().is_empty()
194-
} else {
195-
NeedsDrop::in_any_value_of_ty(cx, ty)
196-
}
179+
let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
180+
let infcx = cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.param_env));
181+
let ocx = ObligationCtxt::new(&infcx);
182+
ocx.register_obligation(Obligation::new(
183+
cx.tcx,
184+
ObligationCause::misc(cx.body.span, cx.def_id()),
185+
cx.param_env,
186+
ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
187+
.to_host_effect_clause(cx.tcx, match cx.const_kind() {
188+
rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
189+
rustc_hir::ConstContext::Static(_) | rustc_hir::ConstContext::Const { .. } => {
190+
ty::BoundConstness::Const
191+
}
192+
}),
193+
));
194+
!ocx.select_all_or_error().is_empty()
197195
}
198196

199197
fn is_structural_in_adt<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,8 @@ declare_features! (
424424
(unstable, const_async_blocks, "1.53.0", Some(85368)),
425425
/// Allows `const || {}` closures in const contexts.
426426
(incomplete, const_closures, "1.68.0", Some(106003)),
427+
/// Uwu
428+
(unstable, const_destruct, "CURRENT_RUSTC_VERSION", Some(10)),
427429
/// Allows `for _ in _` loops in const contexts.
428430
(unstable, const_for, "1.56.0", Some(87575)),
429431
/// Be more precise when looking for live drops in a const context.

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ symbols! {
609609
const_compare_raw_pointers,
610610
const_constructor,
611611
const_deallocate,
612+
const_destruct,
612613
const_eval_limit,
613614
const_eval_select,
614615
const_evaluatable_checked,

library/core/src/marker.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ marker_impls! {
953953
///
954954
/// This should be used for `~const` bounds,
955955
/// as non-const bounds will always hold for every type.
956-
#[unstable(feature = "const_trait_impl", issue = "67792")]
956+
#[unstable(feature = "const_destruct", issue = "10")]
957957
#[lang = "destruct"]
958958
#[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
959959
#[rustc_deny_explicit_impl(implement_via_object = false)]

tests/ui/consts/const-block-const-bound.stderr

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
error[E0658]: use of unstable library feature `const_destruct`
2+
--> $DIR/const-block-const-bound.rs:6:5
3+
|
4+
LL | use std::marker::Destruct;
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
8+
= help: add `#![feature(const_destruct)]` to the crate attributes to enable
9+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
10+
11+
error[E0658]: use of unstable library feature `const_destruct`
12+
--> $DIR/const-block-const-bound.rs:8:22
13+
|
14+
LL | const fn f<T: ~const Destruct>(x: T) {}
15+
| ^^^^^^^^
16+
|
17+
= note: see issue #10 <https://github.com/rust-lang/rust/issues/10> for more information
18+
= help: add `#![feature(const_destruct)]` to the crate attributes to enable
19+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
20+
121
error: `~const` can only be applied to `#[const_trait]` traits
222
--> $DIR/const-block-const-bound.rs:8:15
323
|
@@ -20,6 +40,7 @@ LL | const fn f<T: ~const Destruct>(x: T) {}
2040
| |
2141
| the destructor for this type cannot be evaluated in constant functions
2242

23-
error: aborting due to 3 previous errors
43+
error: aborting due to 5 previous errors
2444

25-
For more information about this error, try `rustc --explain E0493`.
45+
Some errors have detailed explanations: E0493, E0658.
46+
For more information about an error, try `rustc --explain E0493`.

tests/ui/consts/control-flow/drop-fail.precise.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
2-
--> $DIR/drop-fail.rs:8:9
2+
--> $DIR/drop-fail.rs:9:9
33
|
44
LL | let x = Some(Vec::new());
55
| ^ the destructor for this type cannot be evaluated in constants
6+
...
7+
LL | };
8+
| - value is dropped here
69

710
error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
8-
--> $DIR/drop-fail.rs:39:9
11+
--> $DIR/drop-fail.rs:40:9
912
|
1013
LL | let mut tmp = None;
1114
| ^^^^^^^ the destructor for this type cannot be evaluated in constants
15+
...
16+
LL | };
17+
| - value is dropped here
1218

1319
error: aborting due to 2 previous errors
1420

tests/ui/consts/control-flow/drop-fail.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ revisions: stock precise
22

3+
#![feature(const_destruct)]
34
#![cfg_attr(precise, feature(const_precise_live_drops))]
45

56
// `x` is *not* always moved into the final value and may be dropped inside the initializer.

tests/ui/consts/control-flow/drop-fail.stock.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
2-
--> $DIR/drop-fail.rs:8:9
2+
--> $DIR/drop-fail.rs:9:9
33
|
44
LL | let x = Some(Vec::new());
55
| ^ the destructor for this type cannot be evaluated in constants
@@ -8,7 +8,7 @@ LL | };
88
| - value is dropped here
99

1010
error[E0493]: destructor of `(Vec<i32>,)` cannot be evaluated at compile-time
11-
--> $DIR/drop-fail.rs:21:9
11+
--> $DIR/drop-fail.rs:22:9
1212
|
1313
LL | let vec_tuple = (Vec::new(),);
1414
| ^^^^^^^^^ the destructor for this type cannot be evaluated in constants
@@ -17,7 +17,7 @@ LL | };
1717
| - value is dropped here
1818

1919
error[E0493]: destructor of `Result<Vec<i32>, Vec<i32>>` cannot be evaluated at compile-time
20-
--> $DIR/drop-fail.rs:29:9
20+
--> $DIR/drop-fail.rs:30:9
2121
|
2222
LL | let x: Result<_, Vec<i32>> = Ok(Vec::new());
2323
| ^ the destructor for this type cannot be evaluated in constants
@@ -26,7 +26,7 @@ LL | };
2626
| - value is dropped here
2727

2828
error[E0493]: destructor of `Option<Vec<i32>>` cannot be evaluated at compile-time
29-
--> $DIR/drop-fail.rs:39:9
29+
--> $DIR/drop-fail.rs:40:9
3030
|
3131
LL | let mut tmp = None;
3232
| ^^^^^^^ the destructor for this type cannot be evaluated in constants

0 commit comments

Comments
 (0)