Skip to content

Commit 319c790

Browse files
Move auto trait built-in candidate disqualification to a separate method
1 parent b335c2d commit 319c790

File tree

2 files changed

+101
-77
lines changed

2 files changed

+101
-77
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
348348
) {
349349
let lang_items = self.tcx().lang_items();
350350
let trait_def_id = goal.predicate.trait_def_id(self.tcx());
351+
352+
// N.B. When assembling built-in candidates for lang items that are also
353+
// `auto` traits, then the auto trait candidate that is assembled in
354+
// `consider_auto_trait_candidate` MUST be disqualified to remain sound.
355+
//
356+
// Instead of adding the logic here, it's a better idea to add it in
357+
// `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in
358+
// `solve::trait_goals` instead.
351359
let result = if self.tcx().trait_is_auto(trait_def_id) {
352360
G::consider_auto_trait_candidate(self, goal)
353361
} else if self.tcx().trait_is_alias(trait_def_id) {

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+93-77
Original file line numberDiff line numberDiff line change
@@ -147,83 +147,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
147147
ecx: &mut EvalCtxt<'_, 'tcx>,
148148
goal: Goal<'tcx, Self>,
149149
) -> QueryResult<'tcx> {
150-
let self_ty = goal.predicate.self_ty();
151-
match *self_ty.kind() {
152-
// Stall int and float vars until they are resolved to a concrete
153-
// numerical type. That's because the check for impls below treats
154-
// int vars as matching any impl. Even if we filtered such impls,
155-
// we probably don't want to treat an `impl !AutoTrait for i32` as
156-
// disqualifying the built-in auto impl for `i64: AutoTrait` either.
157-
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
158-
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
159-
}
160-
161-
// These types cannot be structurally decomposed into constitutent
162-
// types, and therefore have no builtin impl.
163-
ty::Dynamic(..)
164-
| ty::Param(..)
165-
| ty::Foreign(..)
166-
| ty::Alias(ty::Projection, ..)
167-
| ty::Placeholder(..) => return Err(NoSolution),
168-
169-
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
170-
171-
// Generators have one special built-in candidate, `Unpin`, which
172-
// takes precedence over the structural auto trait candidate being
173-
// assembled.
174-
ty::Generator(_, _, movability)
175-
if Some(goal.predicate.def_id()) == ecx.tcx().lang_items().unpin_trait() =>
176-
{
177-
match movability {
178-
Movability::Static => {
179-
return Err(NoSolution);
180-
}
181-
Movability::Movable => {
182-
return ecx
183-
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
184-
}
185-
}
186-
}
187-
188-
// For rigid types, we only register a builtin auto implementation
189-
// if there is no implementation that could ever apply to the self
190-
// type.
191-
//
192-
// This differs from the current stable behavior and fixes #84857.
193-
// Due to breakage found via crater, we currently instead lint
194-
// patterns which can be used to exploit this unsoundness on stable,
195-
// see #93367 for more details.
196-
ty::Bool
197-
| ty::Char
198-
| ty::Int(_)
199-
| ty::Uint(_)
200-
| ty::Float(_)
201-
| ty::Str
202-
| ty::Array(_, _)
203-
| ty::Slice(_)
204-
| ty::RawPtr(_)
205-
| ty::Ref(_, _, _)
206-
| ty::FnDef(_, _)
207-
| ty::FnPtr(_)
208-
| ty::Closure(_, _)
209-
| ty::Generator(_, _, _)
210-
| ty::GeneratorWitness(_)
211-
| ty::GeneratorWitnessMIR(_, _)
212-
| ty::Never
213-
| ty::Tuple(_)
214-
| ty::Error(_)
215-
| ty::Adt(_, _)
216-
| ty::Alias(ty::Opaque, _) => {
217-
if let Some(def_id) = ecx.tcx().find_map_relevant_impl(
218-
goal.predicate.def_id(),
219-
goal.predicate.self_ty(),
220-
TreatProjections::NextSolverLookup,
221-
Some,
222-
) {
223-
debug!(?def_id, ?goal, "disqualified auto-trait implementation");
224-
return Err(NoSolution);
225-
}
226-
}
150+
if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
151+
return result;
227152
}
228153

229154
ecx.probe_and_evaluate_goal_for_constituent_tys(
@@ -647,6 +572,97 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
647572
}
648573

649574
impl<'tcx> EvalCtxt<'_, 'tcx> {
575+
// Return `Some` if there is an impl (built-in or user provided) that may
576+
// hold for the self type of the goal, which for coherence and soundness
577+
// purposes must disqualify the built-in auto impl assembled by considering
578+
// the type's constituent types.
579+
fn disqualify_auto_trait_candidate_due_to_possible_impl(
580+
&mut self,
581+
goal: Goal<'tcx, TraitPredicate<'tcx>>,
582+
) -> Option<QueryResult<'tcx>> {
583+
let self_ty = goal.predicate.self_ty();
584+
match *self_ty.kind() {
585+
// Stall int and float vars until they are resolved to a concrete
586+
// numerical type. That's because the check for impls below treats
587+
// int vars as matching any impl. Even if we filtered such impls,
588+
// we probably don't want to treat an `impl !AutoTrait for i32` as
589+
// disqualifying the built-in auto impl for `i64: AutoTrait` either.
590+
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
591+
Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS))
592+
}
593+
594+
// These types cannot be structurally decomposed into constitutent
595+
// types, and therefore have no built-in auto impl.
596+
ty::Dynamic(..)
597+
| ty::Param(..)
598+
| ty::Foreign(..)
599+
| ty::Alias(ty::Projection, ..)
600+
| ty::Placeholder(..) => Some(Err(NoSolution)),
601+
602+
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
603+
604+
// Generators have one special built-in candidate, `Unpin`, which
605+
// takes precedence over the structural auto trait candidate being
606+
// assembled.
607+
ty::Generator(_, _, movability)
608+
if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() =>
609+
{
610+
match movability {
611+
Movability::Static => Some(Err(NoSolution)),
612+
Movability::Movable => {
613+
Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
614+
}
615+
}
616+
}
617+
618+
// For rigid types, any possible implementation that could apply to
619+
// the type (even if after unification and processing nested goals
620+
// it does not hold) will disqualify the built-in auto impl.
621+
//
622+
// This differs from the current stable behavior and fixes #84857.
623+
// Due to breakage found via crater, we currently instead lint
624+
// patterns which can be used to exploit this unsoundness on stable,
625+
// see #93367 for more details.
626+
ty::Bool
627+
| ty::Char
628+
| ty::Int(_)
629+
| ty::Uint(_)
630+
| ty::Float(_)
631+
| ty::Str
632+
| ty::Array(_, _)
633+
| ty::Slice(_)
634+
| ty::RawPtr(_)
635+
| ty::Ref(_, _, _)
636+
| ty::FnDef(_, _)
637+
| ty::FnPtr(_)
638+
| ty::Closure(_, _)
639+
| ty::Generator(_, _, _)
640+
| ty::GeneratorWitness(_)
641+
| ty::GeneratorWitnessMIR(_, _)
642+
| ty::Never
643+
| ty::Tuple(_)
644+
| ty::Adt(_, _)
645+
// FIXME: Handling opaques here is kinda sus. Especially because we
646+
// simplify them to PlaceholderSimplifiedType.
647+
| ty::Alias(ty::Opaque, _) => {
648+
if let Some(def_id) = self.tcx().find_map_relevant_impl(
649+
goal.predicate.def_id(),
650+
goal.predicate.self_ty(),
651+
TreatProjections::NextSolverLookup,
652+
Some,
653+
) {
654+
debug!(?def_id, ?goal, "disqualified auto-trait implementation");
655+
// No need to actually consider the candidate here,
656+
// since we do that in `consider_impl_candidate`.
657+
return Some(Err(NoSolution));
658+
} else {
659+
None
660+
}
661+
}
662+
ty::Error(_) => None,
663+
}
664+
}
665+
650666
/// Convenience function for traits that are structural, i.e. that only
651667
/// have nested subgoals that only change the self type. Unlike other
652668
/// evaluate-like helpers, this does a probe, so it doesn't need to be

0 commit comments

Comments
 (0)