use super::Selection;
use super::SelectionResult;
use super::TraitQueryMode;
-use super::{ErrorReporting, Overflow, SelectionError, Unimplemented};
+use super::{ErrorReporting, Overflow, SelectionError};
use super::{ObligationCause, PredicateObligation, TraitObligation};
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
&mut self,
obligation: &TraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
- debug_assert!(!obligation.predicate.has_escaping_bound_vars());
-
- let pec = &ProvisionalEvaluationCache::default();
- let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation);
-
- let candidate = match self.candidate_from_obligation(&stack) {
+ let candidate = match self.select_from_obligation(obligation) {
Err(SelectionError::Overflow) => {
// In standard mode, overflow must have been caught and reported
// earlier.
assert!(self.query_mode == TraitQueryMode::Canonical);
return Err(SelectionError::Overflow);
}
+ Err(SelectionError::Ambiguous(_)) => {
+ return Ok(None);
+ }
Err(e) => {
return Err(e);
}
}
}
+ crate fn select_from_obligation(
+ &mut self,
+ obligation: &TraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+ debug_assert!(!obligation.predicate.has_escaping_bound_vars());
+
+ let pec = &ProvisionalEvaluationCache::default();
+ let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation);
+
+ self.candidate_from_obligation(&stack)
+ }
+
///////////////////////////////////////////////////////////////////////////
// EVALUATION
//
debug!(?fresh_trait_ref);
- if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) {
+ if let Some(result) = self.check_evaluation_cache(
+ obligation.param_env,
+ fresh_trait_ref,
+ obligation.polarity(),
+ ) {
debug!(?result, "CACHE HIT");
return Ok(result);
}
let reached_depth = stack.reached_depth.get();
if reached_depth >= stack.depth {
debug!(?result, "CACHE MISS");
- self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result);
+ self.insert_evaluation_cache(
+ obligation.param_env,
+ fresh_trait_ref,
+ obligation.polarity(),
+ dep_node,
+ result,
+ );
stack.cache().on_completion(stack.dfn, |fresh_trait_ref, provisional_result| {
self.insert_evaluation_cache(
obligation.param_env,
fresh_trait_ref,
+ obligation.polarity(),
dep_node,
provisional_result.max(result),
);
// precise still.
let unbound_input_types =
stack.fresh_trait_ref.value.skip_binder().substs.types().any(|ty| ty.is_fresh());
- // This check was an imperfect workaround for a bug in the old
- // intercrate mode; it should be removed when that goes away.
- if unbound_input_types && self.intercrate {
- debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",);
- // Heuristics: show the diagnostics when there are no candidates in crate.
- if self.intercrate_ambiguity_causes.is_some() {
- debug!("evaluate_stack: intercrate_ambiguity_causes is some");
- if let Ok(candidate_set) = self.assemble_candidates(stack) {
- if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
- let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
- let self_ty = trait_ref.self_ty();
- let cause =
- with_no_trimmed_paths(|| IntercrateAmbiguityCause::DownstreamCrate {
- trait_desc: trait_ref.print_only_trait_path().to_string(),
- self_desc: if self_ty.has_concrete_skeleton() {
- Some(self_ty.to_string())
- } else {
- None
- },
+
+ if stack.obligation.polarity() != ty::ImplPolarity::Negative {
+ // This check was an imperfect workaround for a bug in the old
+ // intercrate mode; it should be removed when that goes away.
+ if unbound_input_types && self.intercrate {
+ debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",);
+ // Heuristics: show the diagnostics when there are no candidates in crate.
+ if self.intercrate_ambiguity_causes.is_some() {
+ debug!("evaluate_stack: intercrate_ambiguity_causes is some");
+ if let Ok(candidate_set) = self.assemble_candidates(stack) {
+ if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
+ let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+ let self_ty = trait_ref.self_ty();
+ let cause = with_no_trimmed_paths(|| {
+ IntercrateAmbiguityCause::DownstreamCrate {
+ trait_desc: trait_ref.print_only_trait_path().to_string(),
+ self_desc: if self_ty.has_concrete_skeleton() {
+ Some(self_ty.to_string())
+ } else {
+ None
+ },
+ }
});
- debug!(?cause, "evaluate_stack: pushing cause");
- self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
+ debug!(?cause, "evaluate_stack: pushing cause");
+ self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
+ }
}
}
+ return Ok(EvaluatedToAmbig);
}
- return Ok(EvaluatedToAmbig);
}
+
if unbound_input_types
&& stack.iter().skip(1).any(|prev| {
stack.obligation.param_env == prev.obligation.param_env
match self.candidate_from_obligation(stack) {
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
+ Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig),
Ok(None) => Ok(EvaluatedToAmbig),
- Err(Overflow) => Err(OverflowError::Cannonical),
+ Err(Overflow) => Err(OverflowError::Canonical),
Err(ErrorReporting) => Err(OverflowError::ErrorReporting),
Err(..) => Ok(EvaluatedToErr),
}
&self,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>,
+ polarity: ty::ImplPolarity,
) -> Option<EvaluationResult> {
// Neither the global nor local cache is aware of intercrate
// mode, so don't do any caching. In particular, we might
let tcx = self.tcx();
if self.can_use_global_caches(param_env) {
- if let Some(res) = tcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) {
+ if let Some(res) = tcx.evaluation_cache.get(&(param_env.and(trait_ref), polarity), tcx)
+ {
return Some(res);
}
}
- self.infcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx)
+ self.infcx.evaluation_cache.get(&(param_env.and(trait_ref), polarity), tcx)
}
fn insert_evaluation_cache(
&mut self,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>,
+ polarity: ty::ImplPolarity,
dep_node: DepNodeIndex,
result: EvaluationResult,
) {
// FIXME: Due to #50507 this overwrites the different values
// This should be changed to use HashMapExt::insert_same
// when that is fixed
- self.tcx().evaluation_cache.insert(param_env.and(trait_ref), dep_node, result);
+ self.tcx().evaluation_cache.insert(
+ (param_env.and(trait_ref), polarity),
+ dep_node,
+ result,
+ );
return;
}
}
debug!(?trait_ref, ?result, "insert_evaluation_cache");
- self.infcx.evaluation_cache.insert(param_env.and(trait_ref), dep_node, result);
+ self.infcx.evaluation_cache.insert((param_env.and(trait_ref), polarity), dep_node, result);
}
/// For various reasons, it's possible for a subobligation
self.infcx.report_overflow_error(error_obligation, true);
}
TraitQueryMode::Canonical => {
- return Err(OverflowError::Cannonical);
+ return Err(OverflowError::Canonical);
}
}
}
(result, dep_node)
}
+ /// filter_impls filters constant trait obligations and candidates that have a positive impl
+ /// for a negative goal and a negative impl for a positive goal
#[instrument(level = "debug", skip(self))]
fn filter_impls(
&mut self,
- candidate: SelectionCandidate<'tcx>,
+ candidates: Vec<SelectionCandidate<'tcx>>,
obligation: &TraitObligation<'tcx>,
- ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+ ) -> Vec<SelectionCandidate<'tcx>> {
let tcx = self.tcx();
- // Respect const trait obligations
- if self.is_trait_predicate_const(obligation.predicate.skip_binder()) {
- match candidate {
- // const impl
- ImplCandidate(def_id) if tcx.impl_constness(def_id) == hir::Constness::Const => {}
- // const param
- ParamCandidate(ty::ConstnessAnd {
- constness: ty::BoundConstness::ConstIfConst,
- ..
- }) => {}
- // auto trait impl
- AutoImplCandidate(..) => {}
- // generator, this will raise error in other places
- // or ignore error with const_async_blocks feature
- GeneratorCandidate => {}
- // FnDef where the function is const
- FnPointerCandidate { is_const: true } => {}
- ConstDropCandidate => {}
- _ => {
- // reject all other types of candidates
- return Err(Unimplemented);
+ let mut result = Vec::with_capacity(candidates.len());
+
+ for candidate in candidates {
+ // Respect const trait obligations
+ if self.is_trait_predicate_const(obligation.predicate.skip_binder()) {
+ match candidate {
+ // const impl
+ ImplCandidate(def_id)
+ if tcx.impl_constness(def_id) == hir::Constness::Const => {}
+ // const param
+ ParamCandidate((
+ ty::ConstnessAnd { constness: ty::BoundConstness::ConstIfConst, .. },
+ _,
+ )) => {}
+ // auto trait impl
+ AutoImplCandidate(..) => {}
+ // generator, this will raise error in other places
+ // or ignore error with const_async_blocks feature
+ GeneratorCandidate => {}
+ // FnDef where the function is const
+ FnPointerCandidate { is_const: true } => {}
+ ConstDropCandidate => {}
+ _ => {
+ // reject all other types of candidates
+ continue;
+ }
}
}
+
+ if let ImplCandidate(def_id) = candidate {
+ if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id)
+ || obligation.polarity() == tcx.impl_polarity(def_id)
+ || self.allow_negative_impls
+ {
+ result.push(candidate);
+ }
+ } else {
+ result.push(candidate);
+ }
}
- // Treat negative impls as unimplemented, and reservation impls as ambiguity.
+
+ result
+ }
+
+ /// filter_reservation_impls filter reservation impl for any goal as ambiguous
+ #[instrument(level = "debug", skip(self))]
+ fn filter_reservation_impls(
+ &mut self,
+ candidate: SelectionCandidate<'tcx>,
+ obligation: &TraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+ let tcx = self.tcx();
+ // Treat reservation impls as ambiguity.
if let ImplCandidate(def_id) = candidate {
- match tcx.impl_polarity(def_id) {
- ty::ImplPolarity::Negative if !self.allow_negative_impls => {
- return Err(Unimplemented);
- }
- ty::ImplPolarity::Reservation => {
- if let Some(intercrate_ambiguity_clauses) =
- &mut self.intercrate_ambiguity_causes
- {
- let attrs = tcx.get_attrs(def_id);
- let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl);
- let value = attr.and_then(|a| a.value_str());
- if let Some(value) = value {
- debug!(
- "filter_impls: \
+ if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) {
+ if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes {
+ let attrs = tcx.get_attrs(def_id);
+ let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl);
+ let value = attr.and_then(|a| a.value_str());
+ if let Some(value) = value {
+ debug!(
+ "filter_reservation_impls: \
reservation impl ambiguity on {:?}",
- def_id
- );
- intercrate_ambiguity_clauses.push(
- IntercrateAmbiguityCause::ReservationImpl {
- message: value.to_string(),
- },
- );
- }
+ def_id
+ );
+ intercrate_ambiguity_clauses.push(
+ IntercrateAmbiguityCause::ReservationImpl {
+ message: value.to_string(),
+ },
+ );
}
- return Ok(None);
}
- _ => {}
- };
+ return Ok(None);
+ }
}
Ok(Some(candidate))
}
fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
debug!("is_knowable(intercrate={:?})", self.intercrate);
- if !self.intercrate {
+ if !self.intercrate || stack.obligation.polarity() == ty::ImplPolarity::Negative {
return None;
}
if self.can_use_global_caches(param_env) {
if let Some(res) = tcx
.selection_cache
- .get(¶m_env.and(trait_ref).with_constness(pred.constness), tcx)
+ .get(&(param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), tcx)
{
return Some(res);
}
}
self.infcx
.selection_cache
- .get(¶m_env.and(trait_ref).with_constness(pred.constness), tcx)
+ .get(&(param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), tcx)
}
/// Determines whether can we safely cache the result
debug!(?trait_ref, ?candidate, "insert_candidate_cache global");
// This may overwrite the cache with the same value.
tcx.selection_cache.insert(
- param_env.and(trait_ref).with_constness(pred.constness),
+ (param_env.and(trait_ref).with_constness(pred.constness), pred.polarity),
dep_node,
candidate,
);
debug!(?trait_ref, ?candidate, "insert_candidate_cache local");
self.infcx.selection_cache.insert(
- param_env.and(trait_ref).with_constness(pred.constness),
+ (param_env.and(trait_ref).with_constness(pred.constness), pred.polarity),
dep_node,
candidate,
);
| ConstDropCandidate,
) => false,
- (ParamCandidate(other), ParamCandidate(victim)) => {
+ (
+ ParamCandidate((other, other_polarity)),
+ ParamCandidate((victim, victim_polarity)),
+ ) => {
let same_except_bound_vars = other.value.skip_binder()
== victim.value.skip_binder()
&& other.constness == victim.constness
+ && other_polarity == victim_polarity
&& !other.value.skip_binder().has_escaping_bound_vars();
if same_except_bound_vars {
// See issue #84398. In short, we can generate multiple ParamCandidates which are
other.value.bound_vars().len() <= victim.value.bound_vars().len()
} else if other.value == victim.value
&& victim.constness == ty::BoundConstness::NotConst
+ && other_polarity == victim_polarity
{
// Drop otherwise equivalent non-const candidates in favor of const candidates.
true
| TraitAliasCandidate(..)
| ObjectCandidate(_)
| ProjectionCandidate(_),
- ) => !is_global(&cand.value),
+ ) => !is_global(&cand.0.value),
(ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => {
// Prefer these to a global where-clause bound
// (see issue #50825).
- is_global(&cand.value)
+ is_global(&cand.0.value)
}
(
ImplCandidate(_)
) => {
// Prefer these to a global where-clause bound
// (see issue #50825).
- is_global(&cand.value) && other.evaluation.must_apply_modulo_regions()
+ is_global(&cand.0.value) && other.evaluation.must_apply_modulo_regions()
}
(ProjectionCandidate(i), ProjectionCandidate(j))