1
Fork 0

do not attempt to prove unknowable goals

This commit is contained in:
lcnr 2024-09-02 14:50:50 +00:00
parent 6199b69c53
commit 6188aae369
5 changed files with 100 additions and 108 deletions

View file

@ -304,6 +304,11 @@ where
let mut candidates = vec![]; let mut candidates = vec![];
if self.solver_mode() == SolverMode::Coherence {
if let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) {
return vec![candidate];
}
}
self.assemble_impl_candidates(goal, &mut candidates); self.assemble_impl_candidates(goal, &mut candidates);
self.assemble_builtin_impl_candidates(goal, &mut candidates); self.assemble_builtin_impl_candidates(goal, &mut candidates);
@ -314,11 +319,8 @@ where
self.assemble_param_env_candidates(goal, &mut candidates); self.assemble_param_env_candidates(goal, &mut candidates);
match self.solver_mode() { if self.solver_mode() == SolverMode::Normal {
SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates), self.discard_impls_shadowed_by_env(goal, &mut candidates);
SolverMode::Coherence => {
self.assemble_coherence_unknowable_candidates(goal, &mut candidates)
}
} }
candidates candidates
@ -682,18 +684,15 @@ where
/// also consider impls which may get added in a downstream or sibling crate /// also consider impls which may get added in a downstream or sibling crate
/// or which an upstream impl may add in a minor release. /// or which an upstream impl may add in a minor release.
/// ///
/// To do so we add an ambiguous candidate in case such an unknown impl could /// To do so we return a single ambiguous candidate in case such an unknown
/// apply to the current goal. /// impl could apply to the current goal.
#[instrument(level = "trace", skip_all)] #[instrument(level = "trace", skip_all)]
fn assemble_coherence_unknowable_candidates<G: GoalKind<D>>( fn consider_coherence_unknowable_candidate<G: GoalKind<D>>(
&mut self, &mut self,
goal: Goal<I, G>, goal: Goal<I, G>,
candidates: &mut Vec<Candidate<I>>, ) -> Result<Candidate<I>, NoSolution> {
) { self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(|ecx| {
let cx = self.cx(); let cx = ecx.cx();
candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(
|ecx| {
let trait_ref = goal.predicate.trait_ref(cx); let trait_ref = goal.predicate.trait_ref(cx);
if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? { if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? {
Err(NoSolution) Err(NoSolution)
@ -712,8 +711,7 @@ where
); );
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} }
}, })
))
} }
/// If there's a where-bound for the current goal, do not use any impl candidates /// If there's a where-bound for the current goal, do not use any impl candidates

View file

@ -29,6 +29,7 @@ use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::InferOk; use crate::infer::InferOk;
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::solve::{deeply_normalize_for_diagnostics, inspect}; use crate::solve::{deeply_normalize_for_diagnostics, inspect};
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::{ use crate::traits::{
util, FulfillmentErrorCode, NormalizeExt, Obligation, ObligationCause, PredicateObligation, util, FulfillmentErrorCode, NormalizeExt, Obligation, ObligationCause, PredicateObligation,
@ -624,14 +625,13 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
// at ambiguous goals, as for others the coherence unknowable candidate // at ambiguous goals, as for others the coherence unknowable candidate
// was irrelevant. // was irrelevant.
match goal.result() { match goal.result() {
Ok(Certainty::Maybe(_)) => {}
Ok(Certainty::Yes) | Err(NoSolution) => return, Ok(Certainty::Yes) | Err(NoSolution) => return,
Ok(Certainty::Maybe(_)) => {}
} }
let Goal { param_env, predicate } = goal.goal();
// For bound predicates we simply call `infcx.enter_forall` // For bound predicates we simply call `infcx.enter_forall`
// and then prove the resulting predicate as a nested goal. // and then prove the resulting predicate as a nested goal.
let Goal { param_env, predicate } = goal.goal();
let trait_ref = match predicate.kind().no_bound_vars() { let trait_ref = match predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref, Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)))
@ -645,7 +645,11 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
_ => return, _ => return,
}; };
// Add ambiguity causes for reservation impls. if trait_ref.references_error() {
return;
}
let mut candidates = goal.candidates();
for cand in goal.candidates() { for cand in goal.candidates() {
if let inspect::ProbeKind::TraitCandidate { if let inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(def_id), source: CandidateSource::Impl(def_id),
@ -664,14 +668,20 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
} }
} }
// Add ambiguity causes for unknowable goals. // We also look for unknowable candidates. In case a goal is unknowable, there's
let mut ambiguity_cause = None; // always exactly 1 candidate.
for cand in goal.candidates() { let Some(cand) = candidates.pop() else {
if let inspect::ProbeKind::TraitCandidate { return;
};
let inspect::ProbeKind::TraitCandidate {
source: CandidateSource::CoherenceUnknowable, source: CandidateSource::CoherenceUnknowable,
result: Ok(_), result: Ok(_),
} = cand.kind() } = cand.kind()
{ else {
return;
};
let lazily_normalize_ty = |mut ty: Ty<'tcx>| { let lazily_normalize_ty = |mut ty: Ty<'tcx>| {
if matches!(ty.kind(), ty::Alias(..)) { if matches!(ty.kind(), ty::Alias(..)) {
let ocx = ObligationCtxt::new(infcx); let ocx = ObligationCtxt::new(infcx);
@ -686,56 +696,40 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
}; };
infcx.probe(|_| { infcx.probe(|_| {
match trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) { let conflict = match trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) {
Err(()) => {} Err(()) => return,
Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"), Ok(Ok(())) => {
Ok(Err(conflict)) => { warn!("expected an unknowable trait ref: {trait_ref:?}");
if !trait_ref.references_error() { return;
// Normalize the trait ref for diagnostics, ignoring any errors if this fails. }
let trait_ref = Ok(Err(conflict)) => conflict,
deeply_normalize_for_diagnostics(infcx, param_env, trait_ref); };
// It is only relevant that a goal is unknowable if it would have otherwise
// failed.
let non_intercrate_infcx = infcx.fork_with_intercrate(false);
if non_intercrate_infcx.predicate_may_hold(&Obligation::new(
infcx.tcx,
ObligationCause::dummy(),
param_env,
predicate,
)) {
return;
}
// Normalize the trait ref for diagnostics, ignoring any errors if this fails.
let trait_ref = deeply_normalize_for_diagnostics(infcx, param_env, trait_ref);
let self_ty = trait_ref.self_ty(); let self_ty = trait_ref.self_ty();
let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty); let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty);
ambiguity_cause = Some(match conflict { self.causes.insert(match conflict {
Conflict::Upstream => { Conflict::Upstream => {
IntercrateAmbiguityCause::UpstreamCrateUpdate { IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty }
trait_ref,
self_ty,
}
} }
Conflict::Downstream => { Conflict::Downstream => {
IntercrateAmbiguityCause::DownstreamCrate { IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty }
trait_ref,
self_ty,
}
} }
}); });
} });
}
}
})
} else {
match cand.result() {
// We only add an ambiguity cause if the goal would otherwise
// result in an error.
//
// FIXME: While this matches the behavior of the
// old solver, it is not the only way in which the unknowable
// candidates *weaken* coherence, they can also force otherwise
// successful normalization to be ambiguous.
Ok(Certainty::Maybe(_) | Certainty::Yes) => {
ambiguity_cause = None;
break;
}
Err(NoSolution) => continue,
}
}
}
if let Some(ambiguity_cause) = ambiguity_cause {
self.causes.insert(ambiguity_cause);
}
} }
} }

View file

@ -58,7 +58,7 @@ pub enum Reveal {
All, All,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SolverMode { pub enum SolverMode {
/// Ordinary trait solving, using everywhere except for coherence. /// Ordinary trait solving, using everywhere except for coherence.
Normal, Normal,

View file

@ -7,7 +7,7 @@ LL |
LL | impl<S: Iterator> MyTrait<S> for (Box<<(MyType,) as Mirror>::Assoc>, S::Item) {} LL | impl<S: Iterator> MyTrait<S> for (Box<<(MyType,) as Mirror>::Assoc>, S::Item) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(Box<(MyType,)>, <_ as Iterator>::Item)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(Box<(MyType,)>, <_ as Iterator>::Item)`
| |
= note: upstream crates may add a new impl of trait `std::clone::Clone` for type `(MyType,)` in future versions = note: upstream crates may add a new impl of trait `std::clone::Clone` for type `std::boxed::Box<(MyType,)>` in future versions
= note: upstream crates may add a new impl of trait `std::marker::Copy` for type `std::boxed::Box<(MyType,)>` in future versions = note: upstream crates may add a new impl of trait `std::marker::Copy` for type `std::boxed::Box<(MyType,)>` in future versions
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

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