Only prefer param-env candidates if they remain non-global after norm
This commit is contained in:
@@ -2,21 +2,24 @@
|
||||
|
||||
pub(super) mod structural_traits;
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use derive_where::derive_where;
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::lang_items::TraitSolverLangItem;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Interner, TypeFoldable, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
|
||||
self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _,
|
||||
TypeVisitor, TypingMode, Upcast as _, elaborate,
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::has_only_region_constraints;
|
||||
use super::trait_goals::TraitGoalProvenVia;
|
||||
use super::{has_only_region_constraints, inspect};
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::inspect::ProbeKind;
|
||||
use crate::solve::{
|
||||
BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
|
||||
MaybeCause, NoSolution, QueryResult,
|
||||
MaybeCause, NoSolution, ParamEnvSource, QueryResult,
|
||||
};
|
||||
|
||||
enum AliasBoundKind {
|
||||
@@ -49,18 +52,6 @@ where
|
||||
|
||||
fn trait_def_id(self, cx: I) -> I::DefId;
|
||||
|
||||
/// Try equating an assumption predicate against a goal's predicate. If it
|
||||
/// holds, then execute the `then` callback, which should do any additional
|
||||
/// work, then produce a response (typically by executing
|
||||
/// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
|
||||
fn probe_and_match_goal_against_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
source: CandidateSource<I>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution>;
|
||||
|
||||
/// Consider a clause, which consists of a "assumption" and some "requirements",
|
||||
/// to satisfy a goal. If the requirements hold, then attempt to satisfy our
|
||||
/// goal by equating it with the assumption.
|
||||
@@ -119,6 +110,67 @@ where
|
||||
alias_ty: ty::AliasTy<I>,
|
||||
) -> Vec<Candidate<I>>;
|
||||
|
||||
fn probe_and_consider_param_env_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
Self::fast_reject_assumption(ecx, goal, assumption)?;
|
||||
|
||||
ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
|
||||
Ok(candidate) => inspect::ProbeKind::TraitCandidate {
|
||||
source: candidate.source,
|
||||
result: Ok(candidate.result),
|
||||
},
|
||||
Err(NoSolution) => inspect::ProbeKind::TraitCandidate {
|
||||
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
result: Err(NoSolution),
|
||||
},
|
||||
})
|
||||
.enter(|ecx| {
|
||||
Self::match_assumption(ecx, goal, assumption)?;
|
||||
let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
|
||||
Ok(Candidate {
|
||||
source,
|
||||
result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Try equating an assumption predicate against a goal's predicate. If it
|
||||
/// holds, then execute the `then` callback, which should do any additional
|
||||
/// work, then produce a response (typically by executing
|
||||
/// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
|
||||
fn probe_and_match_goal_against_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
source: CandidateSource<I>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
Self::fast_reject_assumption(ecx, goal, assumption)?;
|
||||
|
||||
ecx.probe_trait_candidate(source).enter(|ecx| {
|
||||
Self::match_assumption(ecx, goal, assumption)?;
|
||||
then(ecx)
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
|
||||
/// and [`I::DefId`].
|
||||
fn fast_reject_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
) -> Result<(), NoSolution>;
|
||||
|
||||
/// Relate the goal and assumption.
|
||||
fn match_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
) -> Result<(), NoSolution>;
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
@@ -500,14 +552,8 @@ where
|
||||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
|
||||
candidates.extend(G::probe_and_consider_implied_clause(
|
||||
self,
|
||||
CandidateSource::ParamEnv(i),
|
||||
goal,
|
||||
assumption,
|
||||
[],
|
||||
));
|
||||
for assumption in goal.param_env.caller_bounds().iter() {
|
||||
candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,4 +989,76 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn characterize_param_env_assumption(
|
||||
&mut self,
|
||||
param_env: I::ParamEnv,
|
||||
assumption: I::Clause,
|
||||
) -> Result<CandidateSource<I>, NoSolution> {
|
||||
// FIXME:
|
||||
if assumption.has_bound_vars() {
|
||||
return Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal));
|
||||
}
|
||||
|
||||
match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) {
|
||||
ControlFlow::Break(Err(NoSolution)) => Err(NoSolution),
|
||||
ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)),
|
||||
ControlFlow::Continue(()) => Ok(CandidateSource::ParamEnv(ParamEnvSource::Global)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FindParamInClause<'a, 'b, D: SolverDelegate<Interner = I>, I: Interner> {
|
||||
ecx: &'a mut EvalCtxt<'b, D>,
|
||||
param_env: I::ParamEnv,
|
||||
}
|
||||
|
||||
impl<D, I> TypeVisitor<I> for FindParamInClause<'_, '_, D, I>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
type Result = ControlFlow<Result<(), NoSolution>>;
|
||||
|
||||
fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
|
||||
self.ecx.enter_forall(t.clone(), |ecx, v| {
|
||||
v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env })
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
|
||||
let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else {
|
||||
return ControlFlow::Break(Err(NoSolution));
|
||||
};
|
||||
let ty = self.ecx.eager_resolve(ty);
|
||||
|
||||
if let ty::Placeholder(_) = ty.kind() {
|
||||
ControlFlow::Break(Ok(()))
|
||||
} else {
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: I::Const) -> Self::Result {
|
||||
let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else {
|
||||
return ControlFlow::Break(Err(NoSolution));
|
||||
};
|
||||
let ct = self.ecx.eager_resolve(ct);
|
||||
|
||||
if let ty::ConstKind::Placeholder(_) = ct.kind() {
|
||||
ControlFlow::Break(Ok(()))
|
||||
} else {
|
||||
ct.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: I::Region) -> Self::Result {
|
||||
match r.kind() {
|
||||
ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()),
|
||||
ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())),
|
||||
ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,39 +36,40 @@ where
|
||||
self.def_id()
|
||||
}
|
||||
|
||||
fn probe_and_match_goal_against_assumption(
|
||||
fn fast_reject_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
source: rustc_type_ir::solve::CandidateSource<I>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: <I as Interner>::Clause,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
assumption: I::Clause,
|
||||
) -> Result<(), NoSolution> {
|
||||
if let Some(host_clause) = assumption.as_host_effect_clause() {
|
||||
if host_clause.def_id() == goal.predicate.def_id()
|
||||
&& host_clause.constness().satisfies(goal.predicate.constness)
|
||||
{
|
||||
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
goal.predicate.trait_ref.args,
|
||||
host_clause.skip_binder().trait_ref.args,
|
||||
) {
|
||||
return Err(NoSolution);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ecx.probe_trait_candidate(source).enter(|ecx| {
|
||||
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
|
||||
ecx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref,
|
||||
assumption_trait_pred.trait_ref,
|
||||
)?;
|
||||
then(ecx)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
|
||||
Err(NoSolution)
|
||||
}
|
||||
|
||||
fn match_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
) -> Result<(), NoSolution> {
|
||||
let Some(host_clause) = assumption.as_host_effect_clause() else {
|
||||
panic!("fast_reject_assumption should have avoided this");
|
||||
};
|
||||
|
||||
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
|
||||
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register additional assumptions for aliases corresponding to `~const` item bounds.
|
||||
@@ -124,7 +125,7 @@ where
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
impl_def_id: <I as Interner>::DefId,
|
||||
impl_def_id: I::DefId,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let cx = ecx.cx();
|
||||
|
||||
@@ -178,7 +179,7 @@ where
|
||||
|
||||
fn consider_error_guaranteed_candidate(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
_guar: <I as Interner>::ErrorGuaranteed,
|
||||
_guar: I::ErrorGuaranteed,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
||||
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
||||
|
||||
@@ -19,6 +19,7 @@ use tracing::{debug, instrument, trace};
|
||||
use super::has_only_region_constraints;
|
||||
use crate::coherence;
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::resolve::EagerResolver;
|
||||
use crate::solve::inspect::{self, ProofTreeBuilder};
|
||||
use crate::solve::search_graph::SearchGraph;
|
||||
use crate::solve::{
|
||||
@@ -1000,6 +1001,13 @@ where
|
||||
self.delegate.resolve_vars_if_possible(value)
|
||||
}
|
||||
|
||||
pub(super) fn eager_resolve<T>(&self, value: T) -> T
|
||||
where
|
||||
T: TypeFoldable<I>,
|
||||
{
|
||||
value.fold_with(&mut EagerResolver::new(self.delegate))
|
||||
}
|
||||
|
||||
pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs {
|
||||
let args = self.delegate.fresh_args_for_item(def_id);
|
||||
for arg in args.iter() {
|
||||
|
||||
@@ -106,50 +106,50 @@ where
|
||||
self.trait_def_id(cx)
|
||||
}
|
||||
|
||||
fn probe_and_match_goal_against_assumption(
|
||||
fn fast_reject_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
source: CandidateSource<I>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
) -> Result<(), NoSolution> {
|
||||
if let Some(projection_pred) = assumption.as_projection_clause() {
|
||||
if projection_pred.item_def_id() == goal.predicate.def_id() {
|
||||
let cx = ecx.cx();
|
||||
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
goal.predicate.alias.args,
|
||||
projection_pred.skip_binder().projection_term.args,
|
||||
) {
|
||||
return Err(NoSolution);
|
||||
return Ok(());
|
||||
}
|
||||
ecx.probe_trait_candidate(source).enter(|ecx| {
|
||||
let assumption_projection_pred =
|
||||
ecx.instantiate_binder_with_infer(projection_pred);
|
||||
ecx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.alias,
|
||||
assumption_projection_pred.projection_term,
|
||||
)?;
|
||||
|
||||
ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
|
||||
|
||||
// Add GAT where clauses from the trait's definition
|
||||
// FIXME: We don't need these, since these are the type's own WF obligations.
|
||||
ecx.add_goals(
|
||||
GoalSource::AliasWellFormed,
|
||||
cx.own_predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(cx, goal.predicate.alias.args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
then(ecx)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
|
||||
Err(NoSolution)
|
||||
}
|
||||
|
||||
fn match_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
) -> Result<(), NoSolution> {
|
||||
let Some(projection_pred) = assumption.as_projection_clause() else {
|
||||
panic!("fast_reject_assumption should have avoided this");
|
||||
};
|
||||
|
||||
let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred);
|
||||
ecx.eq(goal.param_env, goal.predicate.alias, assumption_projection_pred.projection_term)?;
|
||||
|
||||
ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
|
||||
|
||||
// Add GAT where clauses from the trait's definition
|
||||
// FIXME: We don't need these, since these are the type's own WF obligations.
|
||||
let cx = ecx.cx();
|
||||
ecx.add_goals(
|
||||
GoalSource::AliasWellFormed,
|
||||
cx.own_predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(cx, goal.predicate.alias.args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn consider_additional_alias_assumptions(
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
|
||||
use crate::solve::inspect::ProbeKind;
|
||||
use crate::solve::{
|
||||
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
|
||||
NoSolution, QueryResult,
|
||||
NoSolution, ParamEnvSource,
|
||||
};
|
||||
|
||||
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
|
||||
@@ -125,39 +125,40 @@ where
|
||||
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
||||
}
|
||||
|
||||
fn probe_and_match_goal_against_assumption(
|
||||
fn fast_reject_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
source: CandidateSource<I>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
) -> Result<(), NoSolution> {
|
||||
if let Some(trait_clause) = assumption.as_trait_clause() {
|
||||
if trait_clause.def_id() == goal.predicate.def_id()
|
||||
&& trait_clause.polarity() == goal.predicate.polarity
|
||||
{
|
||||
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
|
||||
goal.predicate.trait_ref.args,
|
||||
trait_clause.skip_binder().trait_ref.args,
|
||||
) {
|
||||
return Err(NoSolution);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ecx.probe_trait_candidate(source).enter(|ecx| {
|
||||
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
|
||||
ecx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref,
|
||||
assumption_trait_pred.trait_ref,
|
||||
)?;
|
||||
then(ecx)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
|
||||
Err(NoSolution)
|
||||
}
|
||||
|
||||
fn match_assumption(
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
assumption: I::Clause,
|
||||
) -> Result<(), NoSolution> {
|
||||
let Some(trait_clause) = assumption.as_trait_clause() else {
|
||||
panic!("fast_reject_assumption should have avoided this");
|
||||
};
|
||||
|
||||
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
|
||||
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn consider_auto_trait_candidate(
|
||||
@@ -1253,10 +1254,9 @@ where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
#[instrument(level = "debug", skip(self, goal), ret)]
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub(super) fn merge_trait_candidates(
|
||||
&mut self,
|
||||
goal: Goal<I, TraitPredicate<I>>,
|
||||
mut candidates: Vec<Candidate<I>>,
|
||||
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
|
||||
if let TypingMode::Coherence = self.typing_mode() {
|
||||
@@ -1284,21 +1284,9 @@ where
|
||||
|
||||
// If there are non-global where-bounds, prefer where-bounds
|
||||
// (including global ones) over everything else.
|
||||
let has_non_global_where_bounds = candidates.iter().any(|c| match c.source {
|
||||
CandidateSource::ParamEnv(idx) => {
|
||||
let where_bound = goal.param_env.caller_bounds().get(idx).unwrap();
|
||||
let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else {
|
||||
unreachable!("expected trait-bound: {where_bound:?}");
|
||||
};
|
||||
|
||||
if trait_pred.has_bound_vars() || !trait_pred.is_global() {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
let has_non_global_where_bounds = candidates
|
||||
.iter()
|
||||
.any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
|
||||
if has_non_global_where_bounds {
|
||||
let where_bounds: Vec<_> = candidates
|
||||
.iter()
|
||||
@@ -1331,13 +1319,16 @@ where
|
||||
// is still reported as being proven-via the param-env so that rigid projections
|
||||
// operate correctly. Otherwise, drop all global where-bounds before merging the
|
||||
// remaining candidates.
|
||||
let proven_via =
|
||||
if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
|
||||
TraitGoalProvenVia::ParamEnv
|
||||
} else {
|
||||
candidates.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(_)));
|
||||
TraitGoalProvenVia::Misc
|
||||
};
|
||||
let proven_via = if candidates
|
||||
.iter()
|
||||
.all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)))
|
||||
{
|
||||
TraitGoalProvenVia::ParamEnv
|
||||
} else {
|
||||
candidates
|
||||
.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)));
|
||||
TraitGoalProvenVia::Misc
|
||||
};
|
||||
|
||||
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
|
||||
if let Some(response) = self.try_merge_responses(&all_candidates) {
|
||||
@@ -1353,7 +1344,7 @@ where
|
||||
goal: Goal<I, TraitPredicate<I>>,
|
||||
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
|
||||
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
|
||||
self.merge_trait_candidates(goal, candidates)
|
||||
self.merge_trait_candidates(candidates)
|
||||
}
|
||||
|
||||
fn try_stall_coroutine_witness(
|
||||
|
||||
Reference in New Issue
Block a user