Skip to content

Commit 915a581

Browse files
committed
Do not promote values with const drop that need to be dropped
Changes from #88558 allowed using `~const Drop` in constants by introducing a new `NeedsNonConstDrop` qualif. The new qualif was also used for promotion purposes, and allowed promotion to happen for values that needs to be dropped but which do have a const drop impl. Since for promoted the drop implementation is never executed, this lead to observable change in behaviour. For example: ```rust struct Panic(); impl const Drop for Panic { fn drop(&mut self) { panic!(); } } fn main() { let _ = &Panic(); } ``` Restore the use of `NeedsDrop` qualif during promotion to avoid the issue.
1 parent 171cbc0 commit 915a581

File tree

6 files changed

+97
-5
lines changed

6 files changed

+97
-5
lines changed

compiler/rustc_const_eval/src/transform/check_consts/check.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::mem;
2222
use std::ops::Deref;
2323

2424
use super::ops::{self, NonConstOp, Status};
25-
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop};
25+
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
2626
use super::resolver::FlowSensitiveAnalysis;
2727
use super::{is_lang_panic_fn, is_lang_special_const_fn, ConstCx, Qualif};
2828
use crate::const_eval::is_unstable_const_fn;
@@ -39,6 +39,7 @@ type QualifResults<'mir, 'tcx, Q> =
3939
#[derive(Default)]
4040
pub struct Qualifs<'mir, 'tcx> {
4141
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
42+
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
4243
needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
4344
indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
4445
}
@@ -70,6 +71,33 @@ impl Qualifs<'mir, 'tcx> {
7071
indirectly_mutable.get().contains(local)
7172
}
7273

74+
/// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
75+
///
76+
/// Only updates the cursor if absolutely necessary
77+
pub fn needs_drop(
78+
&mut self,
79+
ccx: &'mir ConstCx<'mir, 'tcx>,
80+
local: Local,
81+
location: Location,
82+
) -> bool {
83+
let ty = ccx.body.local_decls[local].ty;
84+
if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
85+
return false;
86+
}
87+
88+
let needs_drop = self.needs_drop.get_or_insert_with(|| {
89+
let ConstCx { tcx, body, .. } = *ccx;
90+
91+
FlowSensitiveAnalysis::new(NeedsDrop, ccx)
92+
.into_engine(tcx, &body)
93+
.iterate_to_fixpoint()
94+
.into_results_cursor(&body)
95+
});
96+
97+
needs_drop.seek_before_primary_effect(location);
98+
needs_drop.get().contains(local) || self.indirectly_mutable(ccx, local, location)
99+
}
100+
73101
/// Returns `true` if `local` is `NeedsNonConstDrop` at the given `Location`.
74102
///
75103
/// Only updates the cursor if absolutely necessary
@@ -172,6 +200,7 @@ impl Qualifs<'mir, 'tcx> {
172200
};
173201

174202
ConstQualifs {
203+
needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
175204
needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc),
176205
has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
177206
custom_eq,

compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub fn in_any_value_of_ty(
2121
) -> ConstQualifs {
2222
ConstQualifs {
2323
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
24+
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
2425
needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
2526
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
2627
error_occured,
@@ -98,9 +99,31 @@ impl Qualif for HasMutInterior {
9899
}
99100

100101
/// Constant containing an ADT that implements `Drop`.
101-
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
102-
/// as that might not be a `const fn`, and (b) because implicit promotion would
103-
/// remove side-effects that occur as part of dropping that value.
102+
/// This must be ruled out because implicit promotion would remove side-effects
103+
/// that occur as part of dropping that value. N.B., the implicit promotion has
104+
/// to reject const Drop implementations because even if side-effects are ruled
105+
/// out through other means, the execution of the drop could diverge.
106+
pub struct NeedsDrop;
107+
108+
impl Qualif for NeedsDrop {
109+
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
110+
const IS_CLEARED_ON_MOVE: bool = true;
111+
112+
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
113+
qualifs.needs_drop
114+
}
115+
116+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
117+
ty.needs_drop(cx.tcx, cx.param_env)
118+
}
119+
120+
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
121+
adt.has_dtor(cx.tcx)
122+
}
123+
}
124+
125+
/// Constant containing an ADT that implements non-const `Drop`.
126+
/// This must be ruled out because we cannot run `Drop` during compile-time.
104127
pub struct NeedsNonConstDrop;
105128

106129
impl Qualif for NeedsNonConstDrop {

compiler/rustc_const_eval/src/transform/promote_consts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ impl<'tcx> Validator<'_, 'tcx> {
230230

231231
// We cannot promote things that need dropping, since the promoted value
232232
// would not get dropped.
233-
if self.qualif_local::<qualifs::NeedsNonConstDrop>(place.local) {
233+
if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
234234
return Err(Unpromotable);
235235
}
236236

compiler/rustc_middle/src/mir/query.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ pub struct BorrowCheckResult<'tcx> {
224224
#[derive(Clone, Copy, Debug, Default, TyEncodable, TyDecodable, HashStable)]
225225
pub struct ConstQualifs {
226226
pub has_mut_interior: bool,
227+
pub needs_drop: bool,
227228
pub needs_non_const_drop: bool,
228229
pub custom_eq: bool,
229230
pub error_occured: Option<ErrorReported>,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(const_trait_impl)]
2+
#![feature(const_mut_refs)]
3+
4+
struct A();
5+
6+
impl const Drop for A {
7+
fn drop(&mut self) {}
8+
}
9+
10+
const C: A = A();
11+
12+
fn main() {
13+
let _: &'static A = &A(); //~ ERROR temporary value dropped while borrowed
14+
let _: &'static [A] = &[C]; //~ ERROR temporary value dropped while borrowed
15+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0716]: temporary value dropped while borrowed
2+
--> $DIR/promoted-const-drop.rs:13:26
3+
|
4+
LL | let _: &'static A = &A();
5+
| ---------- ^^^ creates a temporary which is freed while still in use
6+
| |
7+
| type annotation requires that borrow lasts for `'static`
8+
LL | let _: &'static [A] = &[C];
9+
LL | }
10+
| - temporary value is freed at the end of this statement
11+
12+
error[E0716]: temporary value dropped while borrowed
13+
--> $DIR/promoted-const-drop.rs:14:28
14+
|
15+
LL | let _: &'static [A] = &[C];
16+
| ------------ ^^^ creates a temporary which is freed while still in use
17+
| |
18+
| type annotation requires that borrow lasts for `'static`
19+
LL | }
20+
| - temporary value is freed at the end of this statement
21+
22+
error: aborting due to 2 previous errors
23+
24+
For more information about this error, try `rustc --explain E0716`.

0 commit comments

Comments
 (0)