Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e0730c9

Browse files
committedNov 4, 2024
Implement ~const Destruct in new solver
1 parent bd767b5 commit e0730c9

File tree

14 files changed

+258
-73
lines changed

14 files changed

+258
-73
lines changed
 

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

+42-39
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
//!
33
//! See the `Qualif` trait for more info.
44
5+
// FIXME(effects): This API should be really reworked. It's dangerously general for
6+
// having basically only two use-cases that act in different ways.
7+
58
use rustc_errors::ErrorGuaranteed;
69
use rustc_hir::LangItem;
710
use rustc_infer::infer::TyCtxtInferExt;
811
use rustc_middle::mir::*;
9-
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
12+
use rustc_middle::ty::{self, AdtDef, Ty, TypingMode};
1013
use rustc_middle::{bug, mir};
1114
use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
1215
use tracing::instrument;
@@ -59,19 +62,9 @@ pub trait Qualif {
5962
/// It also determines the `Qualif`s for primitive types.
6063
fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;
6164

62-
/// Returns `true` if this `Qualif` is inherent to the given struct or enum.
63-
///
64-
/// By default, `Qualif`s propagate into ADTs in a structural way: An ADT only becomes
65-
/// qualified if part of it is assigned a value with that `Qualif`. However, some ADTs *always*
66-
/// have a certain `Qualif`, regardless of whether their fields have it. For example, a type
67-
/// with a custom `Drop` impl is inherently `NeedsDrop`.
68-
///
69-
/// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound.
70-
fn in_adt_inherently<'tcx>(
71-
cx: &ConstCx<'_, 'tcx>,
72-
adt: AdtDef<'tcx>,
73-
args: GenericArgsRef<'tcx>,
74-
) -> bool;
65+
/// Returns `true` if the `Qualif` is not structural, i.e. that we should not recurse
66+
/// into the operand.
67+
fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool;
7568

7669
/// Returns `true` if this `Qualif` behaves sructurally for pointers and references:
7770
/// the pointer/reference qualifies if and only if the pointee qualifies.
@@ -101,6 +94,11 @@ impl Qualif for HasMutInterior {
10194
return false;
10295
}
10396

97+
// Avoid selecting for `UnsafeCell` either.
98+
if ty.ty_adt_def().is_some_and(|adt| adt.is_unsafe_cell()) {
99+
return true;
100+
}
101+
104102
// We do not use `ty.is_freeze` here, because that requires revealing opaque types, which
105103
// requires borrowck, which in turn will invoke mir_const_qualifs again, causing a cycle error.
106104
// Instead we invoke an obligation context manually, and provide the opaque type inference settings
@@ -125,11 +123,7 @@ impl Qualif for HasMutInterior {
125123
!errors.is_empty()
126124
}
127125

128-
fn in_adt_inherently<'tcx>(
129-
_cx: &ConstCx<'_, 'tcx>,
130-
adt: AdtDef<'tcx>,
131-
_: GenericArgsRef<'tcx>,
132-
) -> bool {
126+
fn is_non_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
133127
// Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
134128
// It arises structurally for all other types.
135129
adt.is_unsafe_cell()
@@ -140,6 +134,7 @@ impl Qualif for HasMutInterior {
140134
}
141135
}
142136

137+
// FIXME(effects): Get rid of this!
143138
/// Constant containing an ADT that implements `Drop`.
144139
/// This must be ruled out because implicit promotion would remove side-effects
145140
/// that occur as part of dropping that value. N.B., the implicit promotion has
@@ -159,11 +154,7 @@ impl Qualif for NeedsDrop {
159154
ty.needs_drop(cx.tcx, cx.param_env)
160155
}
161156

162-
fn in_adt_inherently<'tcx>(
163-
cx: &ConstCx<'_, 'tcx>,
164-
adt: AdtDef<'tcx>,
165-
_: GenericArgsRef<'tcx>,
166-
) -> bool {
157+
fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
167158
adt.has_dtor(cx.tcx)
168159
}
169160

@@ -192,16 +183,32 @@ impl Qualif for NeedsNonConstDrop {
192183
return false;
193184
}
194185

195-
// FIXME(const_trait_impl): Reimplement const drop checking.
196-
NeedsDrop::in_any_value_of_ty(cx, ty)
186+
if cx.tcx.features().const_trait_impl() {
187+
let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
188+
let infcx = cx.tcx.infer_ctxt().build(TypingMode::from_param_env(cx.param_env));
189+
let ocx = ObligationCtxt::new(&infcx);
190+
ocx.register_obligation(Obligation::new(
191+
cx.tcx,
192+
ObligationCause::misc(cx.body.span, cx.def_id()),
193+
cx.param_env,
194+
ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty]))
195+
.to_host_effect_clause(cx.tcx, match cx.const_kind() {
196+
rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
197+
rustc_hir::ConstContext::Static(_)
198+
| rustc_hir::ConstContext::Const { .. } => ty::BoundConstness::Const,
199+
}),
200+
));
201+
!ocx.select_all_or_error().is_empty()
202+
} else {
203+
NeedsDrop::in_any_value_of_ty(cx, ty)
204+
}
197205
}
198206

199-
fn in_adt_inherently<'tcx>(
200-
cx: &ConstCx<'_, 'tcx>,
201-
adt: AdtDef<'tcx>,
202-
_: GenericArgsRef<'tcx>,
203-
) -> bool {
204-
adt.has_non_const_dtor(cx.tcx)
207+
fn is_non_structural<'tcx>(cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>) -> bool {
208+
// Even a `const` dtor may have `~const` bounds that may need to
209+
// be satisfied, so this becomes non-structural as soon as the
210+
// ADT gets a destructor at all.
211+
adt.has_dtor(cx.tcx)
205212
}
206213

207214
fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
@@ -257,14 +264,10 @@ where
257264
Rvalue::Aggregate(kind, operands) => {
258265
// Return early if we know that the struct or enum being constructed is always
259266
// qualified.
260-
if let AggregateKind::Adt(adt_did, _, args, ..) = **kind {
267+
if let AggregateKind::Adt(adt_did, ..) = **kind {
261268
let def = cx.tcx.adt_def(adt_did);
262-
if Q::in_adt_inherently(cx, def, args) {
263-
return true;
264-
}
265-
// Don't do any value-based reasoning for unions.
266-
if def.is_union() && Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) {
267-
return true;
269+
if def.is_union() || Q::is_non_structural(cx, def) {
270+
return Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx));
268271
}
269272
}
270273

‎compiler/rustc_middle/src/ty/adt.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
1818
use rustc_query_system::ich::StableHashingContext;
1919
use rustc_session::DataTypeKind;
2020
use rustc_span::symbol::sym;
21+
use rustc_type_ir::solve::AdtDestructorKind;
2122
use tracing::{debug, info, trace};
2223

2324
use super::{
@@ -232,6 +233,13 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
232233
fn is_fundamental(self) -> bool {
233234
self.is_fundamental()
234235
}
236+
237+
fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
238+
Some(match self.destructor(tcx)?.constness {
239+
hir::Constness::Const => AdtDestructorKind::Const,
240+
hir::Constness::NotConst => AdtDestructorKind::NotConst,
241+
})
242+
}
235243
}
236244

237245
#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
@@ -402,10 +410,6 @@ impl<'tcx> AdtDef<'tcx> {
402410
self.destructor(tcx).is_some()
403411
}
404412

405-
pub fn has_non_const_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
406-
matches!(self.destructor(tcx), Some(Destructor { constness: hir::Constness::NotConst, .. }))
407-
}
408-
409413
/// Asserts this is a struct or union and returns its unique variant.
410414
pub fn non_enum_variant(self) -> &'tcx VariantDef {
411415
assert!(self.is_struct() || self.is_union());

‎compiler/rustc_middle/src/ty/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
382382
self.is_conditionally_const(def_id)
383383
}
384384

385+
fn alias_has_const_conditions(self, def_id: DefId) -> bool {
386+
self.is_conditionally_const(def_id)
387+
}
388+
385389
fn const_conditions(
386390
self,
387391
def_id: DefId,
@@ -666,6 +670,7 @@ bidirectional_lang_item_map! {
666670
CoroutineYield,
667671
Destruct,
668672
DiscriminantKind,
673+
Drop,
669674
DynMetadata,
670675
Fn,
671676
FnMut,

‎compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+92-6
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use tracing::instrument;
1010
use super::assembly::Candidate;
1111
use crate::delegate::SolverDelegate;
1212
use crate::solve::{
13-
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
14-
QueryResult, assembly,
13+
AdtDestructorKind, BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource,
14+
NoSolution, QueryResult, assembly,
1515
};
1616

1717
impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
@@ -84,7 +84,11 @@ where
8484
let cx = ecx.cx();
8585
let mut candidates = vec![];
8686

87-
// FIXME(const_trait_impl): We elaborate here because the implied const bounds
87+
if !ecx.cx().alias_has_const_conditions(alias_ty.def_id) {
88+
return vec![];
89+
}
90+
91+
// FIXME(effects): We elaborate here because the implied const bounds
8892
// aren't necessarily elaborated. We probably should prefix this query
8993
// with `explicit_`...
9094
for clause in elaborate::elaborate(
@@ -404,10 +408,92 @@ where
404408
}
405409

406410
fn consider_builtin_destruct_candidate(
407-
_ecx: &mut EvalCtxt<'_, D>,
408-
_goal: Goal<I, Self>,
411+
ecx: &mut EvalCtxt<'_, D>,
412+
goal: Goal<I, Self>,
409413
) -> Result<Candidate<I>, NoSolution> {
410-
Err(NoSolution)
414+
let cx = ecx.cx();
415+
416+
let self_ty = goal.predicate.self_ty();
417+
418+
let const_conditions = match self_ty.kind() {
419+
// An ADT is `~const Destruct` only if all of the fields are,
420+
// *and* if there is a `Drop` impl, that `Drop` impl is also `~const`.
421+
ty::Adt(adt_def, args) => {
422+
let mut const_conditions: Vec<_> = adt_def
423+
.all_field_tys(cx)
424+
.iter_instantiated(cx, args)
425+
.map(|ty| goal.predicate.trait_ref.with_self_ty(cx, ty))
426+
.collect();
427+
match adt_def.destructor(cx) {
428+
// `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`.
429+
Some(AdtDestructorKind::NotConst) => return Err(NoSolution),
430+
// `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold.
431+
Some(AdtDestructorKind::Const) => {
432+
let drop_def_id = cx.require_lang_item(TraitSolverLangItem::Drop);
433+
let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]);
434+
const_conditions.push(drop_trait_ref);
435+
}
436+
// No `Drop` impl, no need to require anything else.
437+
None => {}
438+
}
439+
const_conditions
440+
}
441+
442+
ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => {
443+
vec![goal.predicate.trait_ref.with_self_ty(cx, ty)]
444+
}
445+
446+
ty::Tuple(tys) => {
447+
tys.iter().map(|ty| goal.predicate.trait_ref.with_self_ty(cx, ty)).collect()
448+
}
449+
450+
// Trivially implement `~const Destruct`
451+
ty::Bool
452+
| ty::Char
453+
| ty::Int(..)
454+
| ty::Uint(..)
455+
| ty::Float(..)
456+
| ty::Str
457+
| ty::RawPtr(..)
458+
| ty::Ref(..)
459+
| ty::FnDef(..)
460+
| ty::FnPtr(..)
461+
| ty::Never
462+
| ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_))
463+
| ty::Error(_) => vec![],
464+
465+
// Coroutines and closures could implement `~const Drop`,
466+
// but they don't really need to right now.
467+
ty::Closure(_, _)
468+
| ty::CoroutineClosure(_, _)
469+
| ty::Coroutine(_, _)
470+
| ty::CoroutineWitness(_, _) => return Err(NoSolution),
471+
472+
ty::Dynamic(..)
473+
| ty::Param(_)
474+
| ty::Alias(..)
475+
| ty::Placeholder(_)
476+
| ty::Foreign(_) => return Err(NoSolution),
477+
478+
ty::Bound(..)
479+
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
480+
panic!("unexpected type `{self_ty:?}`")
481+
}
482+
};
483+
484+
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
485+
ecx.add_goals(
486+
GoalSource::Misc,
487+
const_conditions.into_iter().map(|trait_ref| {
488+
goal.with(
489+
cx,
490+
ty::Binder::dummy(trait_ref)
491+
.to_host_effect_clause(cx, goal.predicate.constness),
492+
)
493+
}),
494+
);
495+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
496+
})
411497
}
412498

413499
fn consider_builtin_transmute_candidate(

‎compiler/rustc_type_ir/src/inherent.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_ast_ir::Mutability;
1111
use crate::elaborate::Elaboratable;
1212
use crate::fold::{TypeFoldable, TypeSuperFoldable};
1313
use crate::relate::Relate;
14-
use crate::solve::Reveal;
14+
use crate::solve::{AdtDestructorKind, Reveal};
1515
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
1616
use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
1717

@@ -537,6 +537,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
537537
fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
538538

539539
fn is_fundamental(self) -> bool;
540+
541+
fn destructor(self, interner: I) -> Option<AdtDestructorKind>;
540542
}
541543

542544
pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {

‎compiler/rustc_type_ir/src/interner.rs

+1
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ pub trait Interner:
225225

226226
fn impl_is_const(self, def_id: Self::DefId) -> bool;
227227
fn fn_is_const(self, def_id: Self::DefId) -> bool;
228+
fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool;
228229
fn const_conditions(
229230
self,
230231
def_id: Self::DefId,

‎compiler/rustc_type_ir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub enum TraitSolverLangItem {
1919
CoroutineYield,
2020
Destruct,
2121
DiscriminantKind,
22+
Drop,
2223
DynMetadata,
2324
Fn,
2425
FnMut,

‎compiler/rustc_type_ir/src/solve/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,10 @@ impl MaybeCause {
326326
}
327327
}
328328
}
329+
330+
/// Indicates that a `impl Drop for Adt` is `const` or not.
331+
#[derive(Debug)]
332+
pub enum AdtDestructorKind {
333+
NotConst,
334+
Const,
335+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.