Skip to content

Commit cdeba02

Browse files
committed
Auto merge of #88558 - fee1-dead:const-drop, r=oli-obk
Const drop The changes are pretty primitive at this point. But at least it works. ^-^ Problems with the current change that I can think of now: - [x] `~const Drop` shouldn't change anything in the non-const world. - [x] types that do not have drop glues shouldn't fail to satisfy `~const Drop` in const contexts. `struct S { a: u8, b: u16 }` This might not fail for `needs_non_const_drop`, but it will fail in `rustc_trait_selection`. - [x] The current change accepts types that have `const Drop` impls but have non-const `Drop` glue. Fixes #88424. Significant Changes: - `~const Drop` is no longer treated as a normal trait bound. In non-const contexts, this bound has no effect, but in const contexts, this restricts the input type and all of its transitive fields to either a) have a `const Drop` impl or b) can be trivially dropped (i.e. no drop glue) - `T: ~const Drop` will not be linted like `T: Drop`. - Instead of recursing and iterating through the type in `rustc_mir::transform::check_consts`, we use the trait system to special case `~const Drop`. See [`rustc_trait_selection::...::candidate_assembly#assemble_const_drop_candidates`](https://github.com/fee1-dead/rust/blob/const-drop/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs#L817) and others. Changes not related to `const Drop`ping and/or changes that are insignificant: - `Node.constness_for_typeck` no longer returns `hir::Constness::Const` for type aliases in traits. This was previously used to hack how we determine default bound constness for items. But because we now use an explicit opt-in, it is no longer needed. - Removed `is_const_impl_raw` query. We have `impl_constness`, and the only existing use of that query uses `HirId`, which means we can just operate it with hir. - `ty::Destructor` now has a field `constness`, which represents the constness of the destructor. r? `@oli-obk`
2 parents c3c0f80 + f749e05 commit cdeba02

File tree

26 files changed

+552
-106
lines changed

26 files changed

+552
-106
lines changed

Diff for: compiler/rustc_ast_passes/src/ast_validation.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1669,7 +1669,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
16691669
walk_list!(self, visit_ty, ty);
16701670
}
16711671
AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, ref body))
1672-
if self.in_const_trait_impl || ctxt == AssocCtxt::Trait =>
1672+
if self.in_const_trait_impl
1673+
|| ctxt == AssocCtxt::Trait
1674+
|| matches!(sig.header.constness, Const::Yes(_)) =>
16731675
{
16741676
self.visit_vis(&item.vis);
16751677
self.visit_ident(item.ident);

Diff for: compiler/rustc_const_eval/src/const_eval/fn_queries.rs

+10-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc_hir as hir;
2-
use rustc_hir::def_id::{DefId, LocalDefId};
2+
use rustc_hir::def_id::DefId;
33
use rustc_middle::hir::map::blocks::FnLikeNode;
44
use rustc_middle::ty::query::Providers;
55
use rustc_middle::ty::TyCtxt;
@@ -34,8 +34,14 @@ pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
3434
}
3535

3636
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
37-
let parent_id = tcx.hir().get_parent_did(hir_id);
38-
if !parent_id.is_top_level_module() { is_const_impl_raw(tcx, parent_id) } else { false }
37+
let parent_id = tcx.hir().get_parent_node(hir_id);
38+
matches!(
39+
tcx.hir().get(parent_id),
40+
hir::Node::Item(hir::Item {
41+
kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }),
42+
..
43+
})
44+
)
3945
}
4046

4147
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
@@ -70,19 +76,6 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
7076
}
7177
}
7278

73-
/// Checks whether the given item is an `impl` that has a `const` modifier.
74-
fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
75-
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
76-
let node = tcx.hir().get(hir_id);
77-
matches!(
78-
node,
79-
hir::Node::Item(hir::Item {
80-
kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }),
81-
..
82-
})
83-
)
84-
}
85-
8679
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
8780
is_const_fn(tcx, def_id)
8881
&& match tcx.lookup_const_stability(def_id) {
@@ -103,10 +96,5 @@ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
10396
}
10497

10598
pub fn provide(providers: &mut Providers) {
106-
*providers = Providers {
107-
is_const_fn_raw,
108-
is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, def_id.expect_local()),
109-
is_promotable_const_fn,
110-
..*providers
111-
};
99+
*providers = Providers { is_const_fn_raw, is_promotable_const_fn, ..*providers };
112100
}

Diff for: compiler/rustc_const_eval/src/transform/check_consts/check.rs

+9-9
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, NeedsDrop};
25+
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop};
2626
use super::resolver::FlowSensitiveAnalysis;
2727
use super::{is_lang_panic_fn, ConstCx, Qualif};
2828
use crate::const_eval::is_unstable_const_fn;
@@ -39,7 +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>>,
42+
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
4343
indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
4444
}
4545

@@ -80,14 +80,14 @@ impl Qualifs<'mir, 'tcx> {
8080
location: Location,
8181
) -> bool {
8282
let ty = ccx.body.local_decls[local].ty;
83-
if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
83+
if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
8484
return false;
8585
}
8686

8787
let needs_drop = self.needs_drop.get_or_insert_with(|| {
8888
let ConstCx { tcx, body, .. } = *ccx;
8989

90-
FlowSensitiveAnalysis::new(NeedsDrop, ccx)
90+
FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
9191
.into_engine(tcx, &body)
9292
.iterate_to_fixpoint()
9393
.into_results_cursor(&body)
@@ -988,12 +988,12 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
988988

989989
let mut err_span = self.span;
990990

991-
// Check to see if the type of this place can ever have a drop impl. If not, this
992-
// `Drop` terminator is frivolous.
993-
let ty_needs_drop =
994-
dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env);
991+
let ty_needs_non_const_drop = qualifs::NeedsNonConstDrop::in_any_value_of_ty(
992+
self.ccx,
993+
dropped_place.ty(self.body, self.tcx).ty,
994+
);
995995

996-
if !ty_needs_drop {
996+
if !ty_needs_non_const_drop {
997997
return;
998998
}
999999

Diff for: compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_span::Span;
55

66
use super::check::Qualifs;
77
use super::ops::{self, NonConstOp};
8-
use super::qualifs::{NeedsDrop, Qualif};
8+
use super::qualifs::{NeedsNonConstDrop, Qualif};
99
use super::ConstCx;
1010

1111
/// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -78,10 +78,10 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
7878
match &terminator.kind {
7979
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
8080
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
81-
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
82-
bug!(
83-
"Drop elaboration left behind a Drop for a type that does not need dropping"
84-
);
81+
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
82+
// Instead of throwing a bug, we just return here. This is because we have to
83+
// run custom `const Drop` impls.
84+
return;
8585
}
8686

8787
if dropped_place.is_indirect() {

Diff for: compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs

+37-7
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
//! See the `Qualif` trait for more info.
44
55
use rustc_errors::ErrorReported;
6+
use rustc_hir as hir;
7+
use rustc_infer::infer::TyCtxtInferExt;
68
use rustc_middle::mir::*;
79
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
810
use rustc_span::DUMMY_SP;
9-
use rustc_trait_selection::traits;
11+
use rustc_trait_selection::traits::{
12+
self, ImplSource, Obligation, ObligationCause, SelectionContext,
13+
};
1014

1115
use super::ConstCx;
1216

@@ -17,7 +21,7 @@ pub fn in_any_value_of_ty(
1721
) -> ConstQualifs {
1822
ConstQualifs {
1923
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
20-
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
24+
needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
2125
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
2226
error_occured,
2327
}
@@ -97,22 +101,48 @@ impl Qualif for HasMutInterior {
97101
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
98102
/// as that might not be a `const fn`, and (b) because implicit promotion would
99103
/// remove side-effects that occur as part of dropping that value.
100-
pub struct NeedsDrop;
104+
pub struct NeedsNonConstDrop;
101105

102-
impl Qualif for NeedsDrop {
103-
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
106+
impl Qualif for NeedsNonConstDrop {
107+
const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop";
104108
const IS_CLEARED_ON_MOVE: bool = true;
105109

106110
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
107111
qualifs.needs_drop
108112
}
109113

110114
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
111-
ty.needs_drop(cx.tcx, cx.param_env)
115+
let drop_trait = if let Some(did) = cx.tcx.lang_items().drop_trait() {
116+
did
117+
} else {
118+
// there is no way to define a type that needs non-const drop
119+
// without having the lang item present.
120+
return false;
121+
};
122+
let trait_ref =
123+
ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) };
124+
let obligation = Obligation::new(
125+
ObligationCause::dummy(),
126+
cx.param_env,
127+
ty::Binder::dummy(ty::TraitPredicate {
128+
trait_ref,
129+
constness: ty::BoundConstness::ConstIfConst,
130+
}),
131+
);
132+
133+
let implsrc = cx.tcx.infer_ctxt().enter(|infcx| {
134+
let mut selcx = SelectionContext::with_constness(&infcx, hir::Constness::Const);
135+
selcx.select(&obligation)
136+
});
137+
match implsrc {
138+
Ok(Some(ImplSource::ConstDrop(_)))
139+
| Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => false,
140+
_ => true,
141+
}
112142
}
113143

114144
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
115-
adt.has_dtor(cx.tcx)
145+
adt.has_non_const_dtor(cx.tcx)
116146
}
117147
}
118148

Diff for: compiler/rustc_const_eval/src/transform/promote_consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ impl<'tcx> Validator<'_, 'tcx> {
231231

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

Diff for: compiler/rustc_hir/src/hir.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -3234,12 +3234,7 @@ impl<'hir> Node<'hir> {
32343234
}
32353235
}
32363236

3237-
/// Returns `Constness::Const` when this node is a const fn/impl/item,
3238-
///
3239-
/// HACK(fee1-dead): or an associated type in a trait. This works because
3240-
/// only typeck cares about const trait predicates, so although the predicates
3241-
/// query would return const predicates when it does not need to be const,
3242-
/// it wouldn't have any effect.
3237+
/// Returns `Constness::Const` when this node is a const fn/impl/item.
32433238
pub fn constness_for_typeck(&self) -> Constness {
32443239
match self {
32453240
Node::Item(Item {
@@ -3258,7 +3253,6 @@ impl<'hir> Node<'hir> {
32583253

32593254
Node::Item(Item { kind: ItemKind::Const(..), .. })
32603255
| Node::TraitItem(TraitItem { kind: TraitItemKind::Const(..), .. })
3261-
| Node::TraitItem(TraitItem { kind: TraitItemKind::Type(..), .. })
32623256
| Node::ImplItem(ImplItem { kind: ImplItemKind::Const(..), .. }) => Constness::Const,
32633257

32643258
_ => Constness::NotConst,

Diff for: compiler/rustc_lint/src/traits.rs

+5
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ declare_lint_pass!(
8686

8787
impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
8888
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
89+
use rustc_middle::ty;
8990
use rustc_middle::ty::PredicateKind::*;
9091

9192
let predicates = cx.tcx.explicit_predicates_of(item.def_id);
@@ -94,6 +95,10 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
9495
Trait(trait_predicate) => trait_predicate,
9596
_ => continue,
9697
};
98+
if trait_predicate.constness == ty::BoundConstness::ConstIfConst {
99+
// `~const Drop` definitely have meanings so avoid linting here.
100+
continue;
101+
}
97102
let def_id = trait_predicate.trait_ref.def_id;
98103
if cx.tcx.lang_items().drop_trait() == Some(def_id) {
99104
// Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.

Diff for: compiler/rustc_middle/src/query/mod.rs

-8
Original file line numberDiff line numberDiff line change
@@ -552,14 +552,6 @@ rustc_queries! {
552552
desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) }
553553
}
554554

555-
/// Returns `true` if this is a const `impl`. **Do not call this function manually.**
556-
///
557-
/// This query caches the base data for the `is_const_impl` helper function, which also
558-
/// takes into account stability attributes (e.g., `#[rustc_const_unstable]`).
559-
query is_const_impl_raw(key: DefId) -> bool {
560-
desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) }
561-
}
562-
563555
query asyncness(key: DefId) -> hir::IsAsync {
564556
desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) }
565557
}

Diff for: compiler/rustc_middle/src/traits/mod.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,9 @@ pub enum ImplSource<'tcx, N> {
529529

530530
/// ImplSource for a trait alias.
531531
TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
532+
533+
/// ImplSource for a `const Drop` implementation.
534+
ConstDrop(ImplSourceConstDropData),
532535
}
533536

534537
impl<'tcx, N> ImplSource<'tcx, N> {
@@ -543,7 +546,8 @@ impl<'tcx, N> ImplSource<'tcx, N> {
543546
ImplSource::Object(d) => d.nested,
544547
ImplSource::FnPointer(d) => d.nested,
545548
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
546-
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
549+
| ImplSource::Pointee(ImplSourcePointeeData)
550+
| ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(),
547551
ImplSource::TraitAlias(d) => d.nested,
548552
ImplSource::TraitUpcasting(d) => d.nested,
549553
}
@@ -560,7 +564,8 @@ impl<'tcx, N> ImplSource<'tcx, N> {
560564
ImplSource::Object(d) => &d.nested[..],
561565
ImplSource::FnPointer(d) => &d.nested[..],
562566
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
563-
| ImplSource::Pointee(ImplSourcePointeeData) => &[],
567+
| ImplSource::Pointee(ImplSourcePointeeData)
568+
| ImplSource::ConstDrop(ImplSourceConstDropData) => &[],
564569
ImplSource::TraitAlias(d) => &d.nested[..],
565570
ImplSource::TraitUpcasting(d) => &d.nested[..],
566571
}
@@ -621,6 +626,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
621626
nested: d.nested.into_iter().map(f).collect(),
622627
})
623628
}
629+
ImplSource::ConstDrop(ImplSourceConstDropData) => {
630+
ImplSource::ConstDrop(ImplSourceConstDropData)
631+
}
624632
}
625633
}
626634
}
@@ -712,6 +720,9 @@ pub struct ImplSourceDiscriminantKindData;
712720
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
713721
pub struct ImplSourcePointeeData;
714722

723+
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
724+
pub struct ImplSourceConstDropData;
725+
715726
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
716727
pub struct ImplSourceTraitAliasData<'tcx, N> {
717728
pub alias_def_id: DefId,

Diff for: compiler/rustc_middle/src/traits/select.rs

+3
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ pub enum SelectionCandidate<'tcx> {
143143
BuiltinObjectCandidate,
144144

145145
BuiltinUnsizeCandidate,
146+
147+
/// Implementation of `const Drop`.
148+
ConstDropCandidate,
146149
}
147150

148151
/// The result of trait evaluation. The order is important

Diff for: compiler/rustc_middle/src/traits/structural_impls.rs

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
3232
super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d),
3333

3434
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
35+
36+
super::ImplSource::ConstDrop(ref d) => write!(f, "{:?}", d),
3537
}
3638
}
3739
}
@@ -125,4 +127,5 @@ TrivialTypeFoldableAndLiftImpls! {
125127
super::IfExpressionCause,
126128
super::ImplSourceDiscriminantKindData,
127129
super::ImplSourcePointeeData,
130+
super::ImplSourceConstDropData,
128131
}

Diff for: compiler/rustc_middle/src/ty/adt.rs

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
77
use rustc_data_structures::fx::FxHashMap;
88
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
99
use rustc_errors::ErrorReported;
10+
use rustc_hir as hir;
1011
use rustc_hir::def::{DefKind, Res};
1112
use rustc_hir::def_id::DefId;
1213
use rustc_index::vec::{Idx, IndexVec};
@@ -288,6 +289,10 @@ impl<'tcx> AdtDef {
288289
self.destructor(tcx).is_some()
289290
}
290291

292+
pub fn has_non_const_dtor(&self, tcx: TyCtxt<'tcx>) -> bool {
293+
matches!(self.destructor(tcx), Some(Destructor { constness: hir::Constness::NotConst, .. }))
294+
}
295+
291296
/// Asserts this is a struct or union and returns its unique variant.
292297
pub fn non_enum_variant(&self) -> &VariantDef {
293298
assert!(self.is_struct() || self.is_union());

Diff for: compiler/rustc_middle/src/ty/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,8 @@ where
13771377
pub struct Destructor {
13781378
/// The `DefId` of the destructor method
13791379
pub did: DefId,
1380+
/// The constness of the destructor method
1381+
pub constness: hir::Constness,
13801382
}
13811383

13821384
bitflags! {

0 commit comments

Comments
 (0)