Skip to content

Commit 743003b

Browse files
committed
Auto merge of #132329 - compiler-errors:fn-and-destruct, r=lcnr
Implement `~const Destruct` effect goal in the new solver This also fixed a subtle bug/limitation of the `NeedsConstDrop` check. Specifically, the "`Qualif`" API basically treats const drops as totally structural, even though dropping something that has an explicit `Drop` implementation cannot be structurally decomposed. For example: ```rust #![feature(const_trait_impl)] #[const_trait] trait Foo { fn foo(); } struct Conditional<T: Foo>(T); impl Foo for () { fn foo() { println!("uh oh"); } } impl<T> const Drop for Conditional<T> where T: ~const Foo { fn drop(&mut self) { T::foo(); } } const FOO: () = { let _ = Conditional(()); //~^ This should error. }; fn main() {} ``` In this example, when checking if the `Conditional(())` rvalue is const-drop, since `Conditional` has a const destructor, we would previously recurse into the `()` value and determine it has nothing to drop, which means that it is considered to *not* need a const drop -- even though dropping `Conditional(())` would mean evaluating the destructor which relies on that `T: const Foo` bound to hold! This could be fixed alternatively by banning any const conditions on `const Drop` impls, but that really sucks -- that means that basically no *interesting* const drop impls could be written. We have the capability to totally and intuitively support the right behavior, which I've implemented here.
2 parents f5be3ca + 69a38de commit 743003b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+524
-230
lines changed

compiler/rustc_const_eval/src/check_consts/check.rs

+40-31
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_span::{Span, Symbol, sym};
2424
use rustc_trait_selection::traits::{
2525
Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
2626
};
27-
use tracing::{debug, instrument, trace};
27+
use tracing::{instrument, trace};
2828

2929
use super::ops::{self, NonConstOp, Status};
3030
use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
@@ -47,7 +47,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
4747
/// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
4848
///
4949
/// Only updates the cursor if absolutely necessary
50-
fn needs_drop(
50+
pub(crate) fn needs_drop(
5151
&mut self,
5252
ccx: &'mir ConstCx<'mir, 'tcx>,
5353
local: Local,
@@ -421,6 +421,43 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
421421

422422
true
423423
}
424+
425+
pub fn check_drop_terminator(
426+
&mut self,
427+
dropped_place: Place<'tcx>,
428+
location: Location,
429+
terminator_span: Span,
430+
) {
431+
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
432+
433+
let needs_drop = if let Some(local) = dropped_place.as_local() {
434+
self.qualifs.needs_drop(self.ccx, local, location)
435+
} else {
436+
qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
437+
};
438+
// If this type doesn't need a drop at all, then there's nothing to enforce.
439+
if !needs_drop {
440+
return;
441+
}
442+
443+
let mut err_span = self.span;
444+
let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
445+
// Use the span where the local was declared as the span of the drop error.
446+
err_span = self.body.local_decls[local].source_info.span;
447+
self.qualifs.needs_non_const_drop(self.ccx, local, location)
448+
} else {
449+
qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place)
450+
};
451+
452+
self.check_op_spanned(
453+
ops::LiveDrop {
454+
dropped_at: terminator_span,
455+
dropped_ty: ty_of_dropped_place,
456+
needs_non_const_drop,
457+
},
458+
err_span,
459+
);
460+
}
424461
}
425462

426463
impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
@@ -866,35 +903,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
866903
return;
867904
}
868905

869-
let mut err_span = self.span;
870-
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
871-
872-
let ty_needs_non_const_drop =
873-
qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
874-
875-
debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);
876-
877-
if !ty_needs_non_const_drop {
878-
return;
879-
}
880-
881-
let needs_non_const_drop = if let Some(local) = dropped_place.as_local() {
882-
// Use the span where the local was declared as the span of the drop error.
883-
err_span = self.body.local_decls[local].source_info.span;
884-
self.qualifs.needs_non_const_drop(self.ccx, local, location)
885-
} else {
886-
true
887-
};
888-
889-
if needs_non_const_drop {
890-
self.check_op_spanned(
891-
ops::LiveDrop {
892-
dropped_at: Some(terminator.source_info.span),
893-
dropped_ty: ty_of_dropped_place,
894-
},
895-
err_span,
896-
);
897-
}
906+
self.check_drop_terminator(*dropped_place, location, terminator.source_info.span);
898907
}
899908

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

compiler/rustc_const_eval/src/check_consts/ops.rs

+33-7
Original file line numberDiff line numberDiff line change
@@ -459,17 +459,43 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm {
459459

460460
#[derive(Debug)]
461461
pub(crate) struct LiveDrop<'tcx> {
462-
pub dropped_at: Option<Span>,
462+
pub dropped_at: Span,
463463
pub dropped_ty: Ty<'tcx>,
464+
pub needs_non_const_drop: bool,
464465
}
465466
impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
467+
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
468+
if self.needs_non_const_drop {
469+
Status::Forbidden
470+
} else {
471+
Status::Unstable {
472+
gate: sym::const_destruct,
473+
gate_already_checked: false,
474+
safe_to_expose_on_stable: false,
475+
is_function_call: false,
476+
}
477+
}
478+
}
479+
466480
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
467-
ccx.dcx().create_err(errors::LiveDrop {
468-
span,
469-
dropped_ty: self.dropped_ty,
470-
kind: ccx.const_kind(),
471-
dropped_at: self.dropped_at,
472-
})
481+
if self.needs_non_const_drop {
482+
ccx.dcx().create_err(errors::LiveDrop {
483+
span,
484+
dropped_ty: self.dropped_ty,
485+
kind: ccx.const_kind(),
486+
dropped_at: self.dropped_at,
487+
})
488+
} else {
489+
ccx.tcx.sess.create_feature_err(
490+
errors::LiveDrop {
491+
span,
492+
dropped_ty: self.dropped_ty,
493+
kind: ccx.const_kind(),
494+
dropped_at: self.dropped_at,
495+
},
496+
sym::const_destruct,
497+
)
498+
}
473499
}
474500
}
475501

compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs

+12-45
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
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;
9-
use super::check::Qualifs;
10-
use super::ops::{self, NonConstOp};
11-
use super::qualifs::{NeedsNonConstDrop, Qualif};
8+
use crate::check_consts::check::Checker;
129
use crate::check_consts::rustc_allow_const_fn_unstable;
1310

1411
/// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -45,29 +42,16 @@ pub fn check_live_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
4542
return;
4643
}
4744

48-
let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
45+
// I know it's not great to be creating a new const checker, but I'd
46+
// rather use it so we can deduplicate the error emitting logic that
47+
// it contains.
48+
let mut visitor = CheckLiveDrops { checker: Checker::new(&ccx) };
4949

5050
visitor.visit_body(body);
5151
}
5252

5353
struct CheckLiveDrops<'mir, 'tcx> {
54-
ccx: &'mir ConstCx<'mir, 'tcx>,
55-
qualifs: Qualifs<'mir, 'tcx>,
56-
}
57-
58-
// So we can access `body` and `tcx`.
59-
impl<'mir, 'tcx> std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
60-
type Target = ConstCx<'mir, 'tcx>;
61-
62-
fn deref(&self) -> &Self::Target {
63-
self.ccx
64-
}
65-
}
66-
67-
impl<'tcx> CheckLiveDrops<'_, 'tcx> {
68-
fn check_live_drop(&self, span: Span, dropped_ty: Ty<'tcx>) {
69-
ops::LiveDrop { dropped_at: None, dropped_ty }.build_error(self.ccx, span).emit();
70-
}
54+
checker: Checker<'mir, 'tcx>,
7155
}
7256

7357
impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
@@ -87,28 +71,11 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
8771

8872
match &terminator.kind {
8973
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
90-
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
91-
92-
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
93-
// Instead of throwing a bug, we just return here. This is because we have to
94-
// run custom `const Drop` impls.
95-
return;
96-
}
97-
98-
if dropped_place.is_indirect() {
99-
self.check_live_drop(terminator.source_info.span, dropped_ty);
100-
return;
101-
}
102-
103-
// Drop elaboration is not precise enough to accept code like
104-
// `tests/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is
105-
// initialized with `None` and never changed, it still emits drop glue.
106-
// Hence we additionally check the qualifs here to allow more code to pass.
107-
if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) {
108-
// Use the span where the dropped local was declared for the error.
109-
let span = self.body.local_decls[dropped_place.local].source_info.span;
110-
self.check_live_drop(span, dropped_ty);
111-
}
74+
self.checker.check_drop_terminator(
75+
*dropped_place,
76+
location,
77+
terminator.source_info.span,
78+
);
11279
}
11380

11481
mir::TerminatorKind::UnwindTerminate(_)

0 commit comments

Comments
 (0)