Skip to content

Commit 2007f50

Browse files
init drop guard and array-unsizing init
1 parent 4f03c5d commit 2007f50

File tree

8 files changed

+278
-17
lines changed

8 files changed

+278
-17
lines changed

compiler/rustc_hir/src/lang_items.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,13 @@ language_item_table! {
356356
InitError, sym::init_error, init_error, Target::AssocTy, GenericRequirement::None;
357357
InitLayout, sym::init_layout, init_layout, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(1);
358358
InitInit, sym::init_init, init_init, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(1);
359+
InitGuard, sym::init_guard, init_guard, Target::Struct, GenericRequirement::Exact(1);
360+
InitGuardNew, sym::init_guard_new, init_guard_new, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
361+
InitGuardRelease, sym::init_guard_release, init_guard_release, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
362+
InitArrayGuard, sym::init_array_guard, init_array_guard, Target::Struct, GenericRequirement::Exact(1);
363+
InitArrayGuardNew, sym::init_array_guard_new, init_array_guard_new, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
364+
InitArrayGuardBump, sym::init_array_guard_bump, init_array_guard_bump, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
365+
InitArrayGuardRelease, sym::init_array_guard_release, init_array_guard_release, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
359366

360367
Termination, sym::termination, termination, Target::Trait, GenericRequirement::None;
361368

compiler/rustc_middle/src/traits/select.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ pub enum SelectionCandidate<'tcx> {
133133
/// place.
134134
TrivialInitCandidate,
135135

136+
/// Implementation of the `Init` trait through implicit array unsizing
137+
/// from a bound in the environment
138+
ArrayUnsizeInitCandidate(ty::PolyTraitPredicate<'tcx>),
139+
136140
/// Implementation of an `AsyncFn`-family trait by one of the anonymous types
137141
/// generated for an `async ||` expression.
138142
AsyncClosureCandidate,

compiler/rustc_span/src/symbol.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,14 @@ symbols! {
12041204
inherent_associated_types,
12051205
inherit,
12061206
init,
1207+
init_array_guard,
1208+
init_array_guard_bump,
1209+
init_array_guard_new,
1210+
init_array_guard_release,
12071211
init_error,
1212+
init_guard,
1213+
init_guard_new,
1214+
init_guard_release,
12081215
init_init,
12091216
init_layout,
12101217
init_trait,

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

Lines changed: 87 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
128128
&mut candidates,
129129
);
130130
}
131+
Some(LangItem::Init) => {
132+
// User defined `Init` comes first but are applicable only on `struct`s and `enums`
133+
self.assemble_candidates_from_impls(obligation, &mut candidates);
134+
self.assemble_candidates_from_object_ty(obligation, &mut candidates);
135+
self.assemble_init_candidates(stack, &mut candidates)?;
136+
}
131137
_ => {
132138
// We re-match here for traits that can have both builtin impls and user written impls.
133139
// After the builtin impls we need to also add user written impls, which we do not want to
@@ -161,9 +167,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
161167
self.assemble_closure_candidates(obligation, &mut candidates);
162168
self.assemble_fn_pointer_candidates(obligation, &mut candidates);
163169
}
164-
Some(LangItem::Init) => {
165-
self.assemble_init_candidates(obligation, &mut candidates);
166-
}
167170
_ => {}
168171
}
169172

@@ -256,14 +259,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
256259
/// supplied to find out whether it is listed among them.
257260
///
258261
/// Never affects the inference environment.
259-
#[instrument(level = "debug", skip(self, stack, candidates))]
262+
#[instrument(level = "debug", skip(self, stack, candidates), fields(?stack.obligation))]
260263
fn assemble_candidates_from_caller_bounds<'o>(
261264
&mut self,
262265
stack: &TraitObligationStack<'o, 'tcx>,
263266
candidates: &mut SelectionCandidateSet<'tcx>,
264267
) -> Result<(), SelectionError<'tcx>> {
265-
debug!(?stack.obligation);
266-
267268
let bounds = stack
268269
.obligation
269270
.param_env
@@ -588,17 +589,87 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
588589
}
589590

590591
#[instrument(level = "debug", skip(self, candidates))]
591-
fn assemble_init_candidates(
592+
fn assemble_init_candidates<'o>(
592593
&mut self,
593-
obligation: &PolyTraitObligation<'tcx>,
594+
stack: &TraitObligationStack<'o, 'tcx>,
594595
candidates: &mut SelectionCandidateSet<'tcx>,
595-
) {
596+
) -> Result<(), SelectionError<'tcx>> {
597+
let obligation = stack.obligation;
596598
let self_ty = obligation.self_ty().skip_binder();
597-
match self_ty.kind() {
598-
ty::Init(_, _) => {
599-
debug!("init candidate");
600-
candidates.vec.push(InitCandidate);
599+
debug!(kind = ?self_ty.kind());
600+
let tcx = self.tcx();
601+
let bounds =
602+
obligation.param_env.caller_bounds().iter().filter_map(|p| p.as_trait_clause()).filter(
603+
|p| {
604+
tcx.is_lang_item(p.def_id(), LangItem::Init)
605+
&& matches!(p.polarity(), ty::PredicatePolarity::Positive)
606+
},
607+
);
608+
if let ty::Init(_, _) = self_ty.kind() {
609+
debug!("init candidate");
610+
candidates.vec.push(InitCandidate);
611+
// Short circuiting
612+
// .. because this is the most specific `Init` impl
613+
// there can be.
614+
// During the confirmation stage we will figure out whether
615+
// an unsizing `Init` should be used
616+
return Ok(());
617+
}
618+
let target_ty = self
619+
.infcx
620+
.resolve_vars_if_possible(obligation.predicate.skip_binder().trait_ref.args.type_at(1));
621+
// Try to take from the typing environment, in order of specificity:
622+
let drcx = DeepRejectCtxt::relate_rigid_rigid(tcx);
623+
match target_ty.kind() {
624+
// 1) S: Init<[T; N]>
625+
// ________________ init_unsize_array
626+
// S: Init<[T]>
627+
&ty::Slice(elem_ty) => {
628+
// todo
629+
let mut candidate = None::<ty::PolyTraitPredicate<'_>>;
630+
for bound in bounds {
631+
let target_ty = bound.skip_binder().trait_ref.args.type_at(1);
632+
let &ty::Array(other_elem_ty, _) = target_ty.kind() else {
633+
continue;
634+
};
635+
if !drcx.types_may_unify(self_ty, bound.self_ty().skip_binder())
636+
|| !drcx.types_may_unify(elem_ty, other_elem_ty)
637+
{
638+
continue;
639+
}
640+
641+
let wc =
642+
self.where_clause_may_apply(stack, bound.map_bound(|b| b.trait_ref))?;
643+
if !wc.may_apply() {
644+
continue;
645+
}
646+
// Okay we have one candidate by unsizing the array
647+
// but rejects when there are multiple different impls.
648+
// There is a potential issue: what if there are multiple `Init<[T; N]>`
649+
// with potentially different `N` or un-unifiable `T`?
650+
// This can become an opportunity to bait-and-switch.
651+
if let Some(candidate) = candidate {
652+
let other_target_ty = candidate.skip_binder().trait_ref.args.type_at(1);
653+
if !drcx.types_may_unify(target_ty, other_target_ty) {
654+
debug!(?candidate, another_candidate = ?bound, "ambiguous array unsizing init");
655+
candidates.ambiguous = true;
656+
return Ok(());
657+
}
658+
// We will keep to the first discovery
659+
} else {
660+
candidate = Some(bound);
661+
}
662+
}
663+
candidates.vec.extend(candidate.map(ArrayUnsizeInitCandidate));
664+
}
665+
&ty::Adt(_def, _args) => {
666+
// NOTE: unify for unsizing ADTs too
601667
}
668+
_ => {}
669+
}
670+
// Next candidate, the identity
671+
match self_ty.kind() {
672+
ty::Init(_, _) => {}
602673
ty::Bool
603674
| ty::Char
604675
| ty::Int(_)
@@ -608,6 +679,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
608679
| ty::Foreign(_)
609680
| ty::Str
610681
| ty::Array(_, _)
682+
| ty::Alias(_, _)
683+
| ty::Param(_)
611684
| ty::Pat(_, _)
612685
| ty::Slice(_)
613686
| ty::RawPtr(_, _)
@@ -621,8 +694,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
621694
| ty::Coroutine(_, _)
622695
| ty::CoroutineWitness(_, _)
623696
| ty::Tuple(_)
624-
| ty::Alias(_, _)
625-
| ty::Param(_)
626697
| ty::Infer(
627698
ty::IntVar(_) | ty::FloatVar(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
628699
)
@@ -632,6 +703,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
632703
}
633704
ty::Never | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => {}
634705
}
706+
Ok(())
635707
}
636708

637709
/// Searches for impls that might apply to `obligation`.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
100100
self.confirm_init_candidate(obligation)?,
101101
),
102102

103+
ArrayUnsizeInitCandidate(_bound) => todo!("pinit: sorry"),
104+
103105
TrivialInitCandidate => ImplSource::Builtin(
104106
BuiltinImplSource::Misc,
105107
self.confirm_trivial_init_candidate(obligation)?,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,6 +1993,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19931993
| AsyncFnKindHelperCandidate
19941994
| CoroutineCandidate
19951995
| InitCandidate
1996+
| ArrayUnsizeInitCandidate(..)
19961997
| TrivialInitCandidate
19971998
| FutureCandidate
19981999
| IteratorCandidate

0 commit comments

Comments
 (0)