Skip to content

Commit 6ed42a7

Browse files
Check const Drop impls considering ConstIfConst bounds
1 parent f9eb0b3 commit 6ed42a7

File tree

7 files changed

+221
-161
lines changed

7 files changed

+221
-161
lines changed

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

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
55
use rustc_errors::ErrorReported;
66
use rustc_infer::infer::TyCtxtInferExt;
7+
use rustc_infer::traits::TraitEngine;
78
use rustc_middle::mir::*;
89
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
910
use rustc_span::DUMMY_SP;
1011
use rustc_trait_selection::traits::{
11-
self, ImplSource, Obligation, ObligationCause, SelectionContext,
12+
self, FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext,
1213
};
1314

1415
use super::ConstCx;
@@ -161,28 +162,46 @@ impl Qualif for NeedsNonConstDrop {
161162
// without having the lang item present.
162163
return false;
163164
};
164-
let trait_ref =
165-
ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) };
165+
166166
let obligation = Obligation::new(
167167
ObligationCause::dummy(),
168168
cx.param_env,
169169
ty::Binder::dummy(ty::TraitPredicate {
170-
trait_ref,
170+
trait_ref: ty::TraitRef {
171+
def_id: drop_trait,
172+
substs: cx.tcx.mk_substs_trait(ty, &[]),
173+
},
171174
constness: ty::BoundConstness::ConstIfConst,
172175
polarity: ty::ImplPolarity::Positive,
173176
}),
174177
);
175178

176-
let implsrc = cx.tcx.infer_ctxt().enter(|infcx| {
179+
cx.tcx.infer_ctxt().enter(|infcx| {
177180
let mut selcx = SelectionContext::new(&infcx);
178-
selcx.select(&obligation)
179-
});
180-
!matches!(
181-
implsrc,
182-
Ok(Some(
181+
let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
182+
// If we couldn't select a const drop candidate, then it's bad
183+
return true;
184+
};
185+
186+
if !matches!(
187+
impl_src,
183188
ImplSource::ConstDrop(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
184-
))
185-
)
189+
) {
190+
// If our const drop candidate is not ConstDrop or implied by param,
191+
// then it's bad
192+
return true;
193+
}
194+
195+
// If we successfully found one, then select all of the predicates
196+
// implied by our const drop impl.
197+
let mut fcx = FulfillmentContext::new();
198+
for nested in impl_src.nested_obligations() {
199+
fcx.register_predicate_obligation(&infcx, nested);
200+
}
201+
202+
// If we had any errors, then it's bad
203+
!fcx.select_all_or_error(&infcx).is_empty()
204+
})
186205
}
187206

188207
fn in_adt_inherently<'tcx>(

compiler/rustc_middle/src/traits/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ pub enum ImplSource<'tcx, N> {
566566
TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
567567

568568
/// ImplSource for a `const Drop` implementation.
569-
ConstDrop(ImplSourceConstDropData),
569+
ConstDrop(ImplSourceConstDropData<N>),
570570
}
571571

572572
impl<'tcx, N> ImplSource<'tcx, N> {
@@ -581,10 +581,10 @@ impl<'tcx, N> ImplSource<'tcx, N> {
581581
ImplSource::Object(d) => d.nested,
582582
ImplSource::FnPointer(d) => d.nested,
583583
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
584-
| ImplSource::Pointee(ImplSourcePointeeData)
585-
| ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(),
584+
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
586585
ImplSource::TraitAlias(d) => d.nested,
587586
ImplSource::TraitUpcasting(d) => d.nested,
587+
ImplSource::ConstDrop(i) => i.nested,
588588
}
589589
}
590590

@@ -599,10 +599,10 @@ impl<'tcx, N> ImplSource<'tcx, N> {
599599
ImplSource::Object(d) => &d.nested,
600600
ImplSource::FnPointer(d) => &d.nested,
601601
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
602-
| ImplSource::Pointee(ImplSourcePointeeData)
603-
| ImplSource::ConstDrop(ImplSourceConstDropData) => &[],
602+
| ImplSource::Pointee(ImplSourcePointeeData) => &[],
604603
ImplSource::TraitAlias(d) => &d.nested,
605604
ImplSource::TraitUpcasting(d) => &d.nested,
605+
ImplSource::ConstDrop(i) => &i.nested,
606606
}
607607
}
608608

@@ -661,9 +661,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
661661
nested: d.nested.into_iter().map(f).collect(),
662662
})
663663
}
664-
ImplSource::ConstDrop(ImplSourceConstDropData) => {
665-
ImplSource::ConstDrop(ImplSourceConstDropData)
666-
}
664+
ImplSource::ConstDrop(i) => ImplSource::ConstDrop(ImplSourceConstDropData {
665+
nested: i.nested.into_iter().map(f).collect(),
666+
}),
667667
}
668668
}
669669
}
@@ -755,8 +755,10 @@ pub struct ImplSourceDiscriminantKindData;
755755
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
756756
pub struct ImplSourcePointeeData;
757757

758-
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
759-
pub struct ImplSourceConstDropData;
758+
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
759+
pub struct ImplSourceConstDropData<N> {
760+
pub nested: Vec<N>,
761+
}
760762

761763
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
762764
pub struct ImplSourceTraitAliasData<'tcx, N> {

compiler/rustc_middle/src/traits/select.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ pub enum SelectionCandidate<'tcx> {
146146

147147
BuiltinUnsizeCandidate,
148148

149-
/// Implementation of `const Drop`.
150-
ConstDropCandidate,
149+
/// Implementation of `const Drop`, optionally from a custom `impl const Drop`.
150+
ConstDropCandidate(Option<DefId>),
151151
}
152152

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

compiler/rustc_middle/src/traits/structural_impls.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,17 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx,
120120
}
121121
}
122122

123+
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDropData<N> {
124+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125+
write!(f, "ImplSourceConstDropData(nested={:?})", self.nested)
126+
}
127+
}
128+
123129
///////////////////////////////////////////////////////////////////////////
124130
// Lift implementations
125131

126132
TrivialTypeFoldableAndLiftImpls! {
127133
super::IfExpressionCause,
128134
super::ImplSourceDiscriminantKindData,
129135
super::ImplSourcePointeeData,
130-
super::ImplSourceConstDropData,
131136
}

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

Lines changed: 62 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
307307
} else if lang_items.drop_trait() == Some(def_id)
308308
&& obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst
309309
{
310-
if obligation.param_env.constness() == hir::Constness::Const {
311-
self.assemble_const_drop_candidates(obligation, stack, &mut candidates)?;
312-
} else {
313-
debug!("passing ~const Drop bound; in non-const context");
314-
// `~const Drop` when we are not in a const context has no effect.
315-
candidates.vec.push(ConstDropCandidate)
316-
}
310+
self.assemble_const_drop_candidates(obligation, &mut candidates);
317311
} else {
318312
if lang_items.clone_trait() == Some(def_id) {
319313
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -918,139 +912,77 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
918912
}
919913
}
920914

921-
fn assemble_const_drop_candidates<'a>(
915+
fn assemble_const_drop_candidates(
922916
&mut self,
923917
obligation: &TraitObligation<'tcx>,
924-
obligation_stack: &TraitObligationStack<'a, 'tcx>,
925918
candidates: &mut SelectionCandidateSet<'tcx>,
926-
) -> Result<(), SelectionError<'tcx>> {
927-
let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)];
928-
929-
while let Some((ty, depth)) = stack.pop() {
930-
let mut noreturn = false;
931-
932-
self.check_recursion_depth(depth, obligation)?;
933-
let mut new_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
934-
let mut copy_obligation =
935-
obligation.with(obligation.predicate.rebind(ty::TraitPredicate {
936-
trait_ref: ty::TraitRef {
937-
def_id: self.tcx().require_lang_item(hir::LangItem::Copy, None),
938-
substs: self.tcx().mk_substs_trait(ty, &[]),
939-
},
940-
constness: ty::BoundConstness::NotConst,
941-
polarity: ty::ImplPolarity::Positive,
942-
}));
943-
copy_obligation.recursion_depth = depth + 1;
944-
self.assemble_candidates_from_impls(&copy_obligation, &mut new_candidates);
945-
let copy_conditions = self.copy_clone_conditions(&copy_obligation);
946-
self.assemble_builtin_bound_candidates(copy_conditions, &mut new_candidates);
947-
let copy_stack = self.push_stack(obligation_stack.list(), &copy_obligation);
948-
self.assemble_candidates_from_caller_bounds(&copy_stack, &mut new_candidates)?;
949-
950-
let const_drop_obligation =
951-
obligation.with(obligation.predicate.rebind(ty::TraitPredicate {
952-
trait_ref: ty::TraitRef {
953-
def_id: self.tcx().require_lang_item(hir::LangItem::Drop, None),
954-
substs: self.tcx().mk_substs_trait(ty, &[]),
955-
},
956-
constness: ty::BoundConstness::ConstIfConst,
957-
polarity: ty::ImplPolarity::Positive,
958-
}));
959-
960-
let const_drop_stack = self.push_stack(obligation_stack.list(), &const_drop_obligation);
961-
self.assemble_candidates_from_caller_bounds(&const_drop_stack, &mut new_candidates)?;
962-
963-
if !new_candidates.vec.is_empty() {
964-
noreturn = true;
965-
}
966-
debug!(?new_candidates.vec, "assemble_const_drop_candidates");
967-
968-
match ty.kind() {
969-
ty::Int(_)
970-
| ty::Uint(_)
971-
| ty::Float(_)
972-
| ty::Infer(ty::IntVar(_))
973-
| ty::Infer(ty::FloatVar(_))
974-
| ty::FnPtr(_)
975-
| ty::Never
976-
| ty::Ref(..)
977-
| ty::FnDef(..)
978-
| ty::RawPtr(_)
979-
| ty::Bool
980-
| ty::Char
981-
| ty::Str
982-
| ty::Foreign(_) => {} // Do nothing. These types satisfy `const Drop`.
983-
984-
ty::Adt(def, subst) => {
985-
let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
986-
self.assemble_candidates_from_impls(
987-
&obligation.with(obligation.predicate.map_bound(|mut pred| {
988-
pred.trait_ref.substs = self.tcx().mk_substs_trait(ty, &[]);
989-
pred
990-
})),
991-
&mut set,
992-
);
993-
stack.extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1)));
994-
995-
debug!(?set.vec, "assemble_const_drop_candidates - ty::Adt");
996-
if set.vec.into_iter().any(|candidate| {
997-
if let SelectionCandidate::ImplCandidate(did) = candidate {
998-
matches!(self.tcx().impl_constness(did), hir::Constness::NotConst)
999-
} else {
1000-
false
1001-
}
1002-
}) {
1003-
if !noreturn {
1004-
// has non-const Drop
1005-
return Ok(());
1006-
}
1007-
debug!("not returning");
1008-
}
1009-
}
1010-
1011-
ty::Array(ty, _) => stack.push((ty, depth + 1)),
1012-
1013-
ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))),
919+
) {
920+
// If the predicate is `~const Drop` in a non-const environment, we don't actually need
921+
// to check anything. We'll short-circuit checking any obligations in confirmation, too.
922+
if obligation.param_env.constness() == hir::Constness::NotConst {
923+
candidates.vec.push(ConstDropCandidate(None));
924+
return;
925+
}
1014926

1015-
ty::Closure(_, substs) => {
1016-
let substs = substs.as_closure();
1017-
let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty());
1018-
stack.push((ty, depth + 1));
1019-
}
927+
let self_ty = self.infcx().shallow_resolve(obligation.self_ty());
928+
match self_ty.skip_binder().kind() {
929+
ty::Opaque(..)
930+
| ty::Dynamic(..)
931+
| ty::Error(_)
932+
| ty::Bound(..)
933+
| ty::Param(_)
934+
| ty::Placeholder(_)
935+
| ty::Never
936+
| ty::Foreign(_)
937+
| ty::Projection(_) => {
938+
// We don't know if these are `~const Drop`, at least
939+
// not structurally... so don't push a candidate.
940+
}
1020941

1021-
ty::Generator(_, substs, _) => {
1022-
let substs = substs.as_generator();
1023-
let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty());
942+
ty::Bool
943+
| ty::Char
944+
| ty::Int(_)
945+
| ty::Uint(_)
946+
| ty::Float(_)
947+
| ty::Infer(ty::IntVar(_))
948+
| ty::Infer(ty::FloatVar(_))
949+
| ty::Str
950+
| ty::RawPtr(_)
951+
| ty::Ref(..)
952+
| ty::FnDef(..)
953+
| ty::FnPtr(_)
954+
| ty::Array(..)
955+
| ty::Slice(_)
956+
| ty::Closure(..)
957+
| ty::Generator(..)
958+
| ty::Tuple(_)
959+
| ty::GeneratorWitness(_) => {
960+
// These are built-in, and cannot have a custom `impl const Drop`.
961+
candidates.vec.push(ConstDropCandidate(None));
962+
}
1024963

1025-
stack.push((ty, depth + 1));
1026-
stack.push((substs.witness(), depth + 1));
1027-
}
964+
ty::Adt(..) => {
965+
// Find a custom `impl Drop` impl, if it exists
966+
let relevant_impl = self.tcx().find_map_relevant_impl(
967+
obligation.predicate.def_id(),
968+
obligation.predicate.skip_binder().trait_ref.self_ty(),
969+
Some,
970+
);
1028971

1029-
ty::GeneratorWitness(tys) => stack.extend(
1030-
self.tcx().erase_late_bound_regions(*tys).iter().map(|t| (t, depth + 1)),
1031-
),
1032-
1033-
ty::Slice(ty) => stack.push((ty, depth + 1)),
1034-
1035-
ty::Opaque(..)
1036-
| ty::Dynamic(..)
1037-
| ty::Error(_)
1038-
| ty::Bound(..)
1039-
| ty::Infer(_)
1040-
| ty::Placeholder(_)
1041-
| ty::Projection(..)
1042-
| ty::Param(..) => {
1043-
if !noreturn {
1044-
return Ok(());
972+
if let Some(impl_def_id) = relevant_impl {
973+
// Check that `impl Drop` is actually const, if there is a custom impl
974+
if self.tcx().impl_constness(impl_def_id) == hir::Constness::Const {
975+
candidates.vec.push(ConstDropCandidate(Some(impl_def_id)));
1045976
}
1046-
debug!("not returning");
977+
} else {
978+
// Otherwise check the ADT like a built-in type (structurally)
979+
candidates.vec.push(ConstDropCandidate(None));
1047980
}
1048981
}
1049-
debug!(?stack, "assemble_const_drop_candidates - in loop");
1050-
}
1051-
// all types have passed.
1052-
candidates.vec.push(ConstDropCandidate);
1053982

1054-
Ok(())
983+
ty::Infer(_) => {
984+
candidates.ambiguous = true;
985+
}
986+
}
1055987
}
1056988
}

0 commit comments

Comments
 (0)