Skip to content

Commit 6188aae

Browse files
committed
do not attempt to prove unknowable goals
1 parent 6199b69 commit 6188aae

File tree

5 files changed

+101
-109
lines changed

5 files changed

+101
-109
lines changed

Diff for: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+32-34
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,11 @@ where
304304

305305
let mut candidates = vec![];
306306

307+
if self.solver_mode() == SolverMode::Coherence {
308+
if let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) {
309+
return vec![candidate];
310+
}
311+
}
307312
self.assemble_impl_candidates(goal, &mut candidates);
308313

309314
self.assemble_builtin_impl_candidates(goal, &mut candidates);
@@ -314,11 +319,8 @@ where
314319

315320
self.assemble_param_env_candidates(goal, &mut candidates);
316321

317-
match self.solver_mode() {
318-
SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates),
319-
SolverMode::Coherence => {
320-
self.assemble_coherence_unknowable_candidates(goal, &mut candidates)
321-
}
322+
if self.solver_mode() == SolverMode::Normal {
323+
self.discard_impls_shadowed_by_env(goal, &mut candidates);
322324
}
323325

324326
candidates
@@ -682,38 +684,34 @@ where
682684
/// also consider impls which may get added in a downstream or sibling crate
683685
/// or which an upstream impl may add in a minor release.
684686
///
685-
/// To do so we add an ambiguous candidate in case such an unknown impl could
686-
/// apply to the current goal.
687+
/// To do so we return a single ambiguous candidate in case such an unknown
688+
/// impl could apply to the current goal.
687689
#[instrument(level = "trace", skip_all)]
688-
fn assemble_coherence_unknowable_candidates<G: GoalKind<D>>(
690+
fn consider_coherence_unknowable_candidate<G: GoalKind<D>>(
689691
&mut self,
690692
goal: Goal<I, G>,
691-
candidates: &mut Vec<Candidate<I>>,
692-
) {
693-
let cx = self.cx();
694-
695-
candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(
696-
|ecx| {
697-
let trait_ref = goal.predicate.trait_ref(cx);
698-
if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? {
699-
Err(NoSolution)
700-
} else {
701-
// While the trait bound itself may be unknowable, we may be able to
702-
// prove that a super trait is not implemented. For this, we recursively
703-
// prove the super trait bounds of the current goal.
704-
//
705-
// We skip the goal itself as that one would cycle.
706-
let predicate: I::Predicate = trait_ref.upcast(cx);
707-
ecx.add_goals(
708-
GoalSource::Misc,
709-
elaborate::elaborate(cx, [predicate])
710-
.skip(1)
711-
.map(|predicate| goal.with(cx, predicate)),
712-
);
713-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
714-
}
715-
},
716-
))
693+
) -> Result<Candidate<I>, NoSolution> {
694+
self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(|ecx| {
695+
let cx = ecx.cx();
696+
let trait_ref = goal.predicate.trait_ref(cx);
697+
if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? {
698+
Err(NoSolution)
699+
} else {
700+
// While the trait bound itself may be unknowable, we may be able to
701+
// prove that a super trait is not implemented. For this, we recursively
702+
// prove the super trait bounds of the current goal.
703+
//
704+
// We skip the goal itself as that one would cycle.
705+
let predicate: I::Predicate = trait_ref.upcast(cx);
706+
ecx.add_goals(
707+
GoalSource::Misc,
708+
elaborate::elaborate(cx, [predicate])
709+
.skip(1)
710+
.map(|predicate| goal.with(cx, predicate)),
711+
);
712+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
713+
}
714+
})
717715
}
718716

719717
/// If there's a where-bound for the current goal, do not use any impl candidates

Diff for: compiler/rustc_trait_selection/src/traits/coherence.rs

+66-72
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::infer::outlives::env::OutlivesEnvironment;
2929
use crate::infer::InferOk;
3030
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
3131
use crate::solve::{deeply_normalize_for_diagnostics, inspect};
32+
use crate::traits::query::evaluate_obligation::InferCtxtExt;
3233
use crate::traits::select::IntercrateAmbiguityCause;
3334
use crate::traits::{
3435
util, FulfillmentErrorCode, NormalizeExt, Obligation, ObligationCause, PredicateObligation,
@@ -624,14 +625,13 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
624625
// at ambiguous goals, as for others the coherence unknowable candidate
625626
// was irrelevant.
626627
match goal.result() {
627-
Ok(Certainty::Maybe(_)) => {}
628628
Ok(Certainty::Yes) | Err(NoSolution) => return,
629+
Ok(Certainty::Maybe(_)) => {}
629630
}
630631

631-
let Goal { param_env, predicate } = goal.goal();
632-
633632
// For bound predicates we simply call `infcx.enter_forall`
634633
// and then prove the resulting predicate as a nested goal.
634+
let Goal { param_env, predicate } = goal.goal();
635635
let trait_ref = match predicate.kind().no_bound_vars() {
636636
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
637637
Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)))
@@ -645,7 +645,11 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
645645
_ => return,
646646
};
647647

648-
// Add ambiguity causes for reservation impls.
648+
if trait_ref.references_error() {
649+
return;
650+
}
651+
652+
let mut candidates = goal.candidates();
649653
for cand in goal.candidates() {
650654
if let inspect::ProbeKind::TraitCandidate {
651655
source: CandidateSource::Impl(def_id),
@@ -664,78 +668,68 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
664668
}
665669
}
666670

667-
// Add ambiguity causes for unknowable goals.
668-
let mut ambiguity_cause = None;
669-
for cand in goal.candidates() {
670-
if let inspect::ProbeKind::TraitCandidate {
671-
source: CandidateSource::CoherenceUnknowable,
672-
result: Ok(_),
673-
} = cand.kind()
674-
{
675-
let lazily_normalize_ty = |mut ty: Ty<'tcx>| {
676-
if matches!(ty.kind(), ty::Alias(..)) {
677-
let ocx = ObligationCtxt::new(infcx);
678-
ty = ocx
679-
.structurally_normalize(&ObligationCause::dummy(), param_env, ty)
680-
.map_err(|_| ())?;
681-
if !ocx.select_where_possible().is_empty() {
682-
return Err(());
683-
}
684-
}
685-
Ok(ty)
686-
};
671+
// We also look for unknowable candidates. In case a goal is unknowable, there's
672+
// always exactly 1 candidate.
673+
let Some(cand) = candidates.pop() else {
674+
return;
675+
};
687676

688-
infcx.probe(|_| {
689-
match trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) {
690-
Err(()) => {}
691-
Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"),
692-
Ok(Err(conflict)) => {
693-
if !trait_ref.references_error() {
694-
// Normalize the trait ref for diagnostics, ignoring any errors if this fails.
695-
let trait_ref =
696-
deeply_normalize_for_diagnostics(infcx, param_env, trait_ref);
697-
698-
let self_ty = trait_ref.self_ty();
699-
let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty);
700-
ambiguity_cause = Some(match conflict {
701-
Conflict::Upstream => {
702-
IntercrateAmbiguityCause::UpstreamCrateUpdate {
703-
trait_ref,
704-
self_ty,
705-
}
706-
}
707-
Conflict::Downstream => {
708-
IntercrateAmbiguityCause::DownstreamCrate {
709-
trait_ref,
710-
self_ty,
711-
}
712-
}
713-
});
714-
}
715-
}
716-
}
717-
})
718-
} else {
719-
match cand.result() {
720-
// We only add an ambiguity cause if the goal would otherwise
721-
// result in an error.
722-
//
723-
// FIXME: While this matches the behavior of the
724-
// old solver, it is not the only way in which the unknowable
725-
// candidates *weaken* coherence, they can also force otherwise
726-
// successful normalization to be ambiguous.
727-
Ok(Certainty::Maybe(_) | Certainty::Yes) => {
728-
ambiguity_cause = None;
729-
break;
730-
}
731-
Err(NoSolution) => continue,
677+
let inspect::ProbeKind::TraitCandidate {
678+
source: CandidateSource::CoherenceUnknowable,
679+
result: Ok(_),
680+
} = cand.kind()
681+
else {
682+
return;
683+
};
684+
685+
let lazily_normalize_ty = |mut ty: Ty<'tcx>| {
686+
if matches!(ty.kind(), ty::Alias(..)) {
687+
let ocx = ObligationCtxt::new(infcx);
688+
ty = ocx
689+
.structurally_normalize(&ObligationCause::dummy(), param_env, ty)
690+
.map_err(|_| ())?;
691+
if !ocx.select_where_possible().is_empty() {
692+
return Err(());
732693
}
733694
}
734-
}
695+
Ok(ty)
696+
};
735697

736-
if let Some(ambiguity_cause) = ambiguity_cause {
737-
self.causes.insert(ambiguity_cause);
738-
}
698+
infcx.probe(|_| {
699+
let conflict = match trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) {
700+
Err(()) => return,
701+
Ok(Ok(())) => {
702+
warn!("expected an unknowable trait ref: {trait_ref:?}");
703+
return;
704+
}
705+
Ok(Err(conflict)) => conflict,
706+
};
707+
708+
// It is only relevant that a goal is unknowable if it would have otherwise
709+
// failed.
710+
let non_intercrate_infcx = infcx.fork_with_intercrate(false);
711+
if non_intercrate_infcx.predicate_may_hold(&Obligation::new(
712+
infcx.tcx,
713+
ObligationCause::dummy(),
714+
param_env,
715+
predicate,
716+
)) {
717+
return;
718+
}
719+
720+
// Normalize the trait ref for diagnostics, ignoring any errors if this fails.
721+
let trait_ref = deeply_normalize_for_diagnostics(infcx, param_env, trait_ref);
722+
let self_ty = trait_ref.self_ty();
723+
let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty);
724+
self.causes.insert(match conflict {
725+
Conflict::Upstream => {
726+
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty }
727+
}
728+
Conflict::Downstream => {
729+
IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty }
730+
}
731+
});
732+
});
739733
}
740734
}
741735

Diff for: compiler/rustc_type_ir/src/solve/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub enum Reveal {
5858
All,
5959
}
6060

61-
#[derive(Debug, Clone, Copy)]
61+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6262
pub enum SolverMode {
6363
/// Ordinary trait solving, using everywhere except for coherence.
6464
Normal,

Diff for: tests/ui/coherence/normalize-for-errors.next.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL |
77
LL | impl<S: Iterator> MyTrait<S> for (Box<<(MyType,) as Mirror>::Assoc>, S::Item) {}
88
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(Box<(MyType,)>, <_ as Iterator>::Item)`
99
|
10-
= note: upstream crates may add a new impl of trait `std::clone::Clone` for type `(MyType,)` in future versions
10+
= note: upstream crates may add a new impl of trait `std::clone::Clone` for type `std::boxed::Box<(MyType,)>` in future versions
1111
= note: upstream crates may add a new impl of trait `std::marker::Copy` for type `std::boxed::Box<(MyType,)>` in future versions
1212

1313
error: aborting due to 1 previous error

Diff for: tests/ui/coherence/normalize-for-errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ impl<S: Iterator> MyTrait<S> for (Box<<(MyType,) as Mirror>::Assoc>, S::Item) {}
1818
//~^ ERROR conflicting implementations of trait `MyTrait<_>` for type `(Box<(MyType,)>,
1919
//~| NOTE conflicting implementation for `(Box<(MyType,)>,
2020
//~| NOTE upstream crates may add a new impl of trait `std::marker::Copy` for type `std::boxed::Box<(MyType,)>` in future versions
21-
//[next]~| NOTE upstream crates may add a new impl of trait `std::clone::Clone` for type `(MyType,)` in future versions
21+
//[next]~| NOTE upstream crates may add a new impl of trait `std::clone::Clone` for type `std::boxed::Box<(MyType,)>` in future versions
2222

2323
fn main() {}

0 commit comments

Comments
 (0)