use rustc_hir as hir;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TypeFoldable};
use rustc_target::spec::abi::Abi;
use crate::traits::coherence::Conflict;
use crate::traits::{util, SelectionResult};
use crate::traits::{Overflow, Unimplemented};
use super::BuiltinImplConditions;
use super::IntercrateAmbiguityCause;
use super::OverflowError;
use super::SelectionCandidate::{self, *};
use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self))]
pub(super) fn candidate_from_obligation<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate);
debug!(?cache_fresh_trait_pred);
debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
if let Some(c) =
self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
{
debug!(candidate = ?c, "CACHE HIT");
return c;
}
let (candidate, dep_node) =
self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
debug!(?candidate, "CACHE MISS");
self.insert_candidate_cache(
stack.obligation.param_env,
cache_fresh_trait_pred,
dep_node,
candidate.clone(),
);
candidate
}
fn candidate_from_obligation_no_cache<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
if let Some(conflict) = self.is_knowable(stack) {
debug!("coherence stage: not knowable");
if self.intercrate_ambiguity_causes.is_some() {
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
if let Ok(candidate_set) = self.assemble_candidates(stack) {
let mut no_candidates_apply = true;
for c in candidate_set.vec.iter() {
if self.evaluate_candidate(stack, &c)?.may_apply() {
no_candidates_apply = false;
break;
}
}
if !candidate_set.ambiguous && no_candidates_apply {
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
let self_ty = trait_ref.self_ty();
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
let trait_desc = trait_ref.print_only_trait_path().to_string();
let self_desc = if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
};
(trait_desc, self_desc)
});
let cause = if let Conflict::Upstream = conflict {
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
} else {
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
};
debug!(?cause, "evaluate_stack: pushing cause");
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
}
}
}
return Ok(None);
}
let candidate_set = self.assemble_candidates(stack)?;
if candidate_set.ambiguous {
debug!("candidate set contains ambig");
return Ok(None);
}
let mut candidates = candidate_set.vec;
debug!(?stack, ?candidates, "assembled {} candidates", candidates.len());
if candidates.len() == 1 {
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
}
let mut candidates = candidates
.into_iter()
.map(|c| match self.evaluate_candidate(stack, &c) {
Ok(eval) if eval.may_apply() => {
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
}
Ok(_) => Ok(None),
Err(OverflowError) => Err(Overflow),
})
.flat_map(Result::transpose)
.collect::<Result<Vec<_>, _>>()?;
debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
let needs_infer = stack.obligation.predicate.has_infer_types_or_consts();
if candidates.len() > 1 {
let mut i = 0;
while i < candidates.len() {
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
self.candidate_should_be_dropped_in_favor_of(
&candidates[i],
&candidates[j],
needs_infer,
)
});
if is_dup {
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
candidates.swap_remove(i);
} else {
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
i += 1;
if i > 1 {
debug!("multiple matches, ambig");
return Ok(None);
}
}
}
}
if candidates.is_empty() {
if stack.obligation.references_error() {
debug!("no results for error type, treating as ambiguous");
return Ok(None);
}
return Err(Unimplemented);
}
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
}
pub(super) fn assemble_candidates<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>> {
let TraitObligationStack { obligation, .. } = *stack;
let obligation = &Obligation {
param_env: obligation.param_env,
cause: obligation.cause.clone(),
recursion_depth: obligation.recursion_depth,
predicate: self.infcx().resolve_vars_if_possible(obligation.predicate),
};
if obligation.predicate.skip_binder().self_ty().is_ty_var() {
return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true });
}
let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?;
let def_id = obligation.predicate.def_id();
let lang_items = self.tcx().lang_items();
if lang_items.copy_trait() == Some(def_id) {
debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty());
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
let copy_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?;
} else if lang_items.discriminant_kind_trait() == Some(def_id) {
candidates.vec.push(DiscriminantKindCandidate);
} else if lang_items.sized_trait() == Some(def_id) {
let sized_conditions = self.sized_conditions(obligation);
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?;
} else if lang_items.unsize_trait() == Some(def_id) {
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else {
if lang_items.clone_trait() == Some(def_id) {
let clone_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?;
}
self.assemble_generator_candidates(obligation, &mut candidates)?;
self.assemble_closure_candidates(obligation, &mut candidates)?;
self.assemble_fn_pointer_candidates(obligation, &mut candidates)?;
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
self.assemble_candidates_from_object_ty(obligation, &mut candidates);
}
self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
if candidates.vec.is_empty() {
self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?;
}
debug!("candidate list size: {}", candidates.vec.len());
Ok(candidates)
}
fn assemble_candidates_from_projected_tys(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
debug!(?obligation, "assemble_candidates_from_projected_tys");
match obligation.predicate.skip_binder().trait_ref.self_ty().kind() {
ty::Projection(_) | ty::Opaque(..) => {}
ty::Infer(ty::TyVar(_)) => {
span_bug!(
obligation.cause.span,
"Self=_ should have been handled by assemble_candidates"
);
}
_ => return,
}
let result = self
.infcx
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
for predicate_index in result {
candidates.vec.push(ProjectionCandidate(predicate_index));
}
}
fn assemble_candidates_from_caller_bounds<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
debug!(?stack.obligation, "assemble_candidates_from_caller_bounds");
let all_bounds = stack
.obligation
.param_env
.caller_bounds()
.iter()
.filter_map(|o| o.to_opt_poly_trait_ref());
let matching_bounds =
all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
for bound in matching_bounds {
let wc = self.evaluate_where_clause(stack, bound)?;
if wc.may_apply() {
candidates.vec.push(ParamCandidate(bound));
}
}
Ok(())
}
fn assemble_generator_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) {
return Ok(());
}
let self_ty = obligation.self_ty().skip_binder();
match self_ty.kind() {
ty::Generator(..) => {
debug!(?self_ty, ?obligation, "assemble_generator_candidates",);
candidates.vec.push(GeneratorCandidate);
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_generator_candidates: ambiguous self-type");
candidates.ambiguous = true;
}
_ => {}
}
Ok(())
}
fn assemble_closure_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) {
Some(k) => k,
None => {
return Ok(());
}
};
match *obligation.self_ty().skip_binder().kind() {
ty::Closure(_, closure_substs) => {
debug!(?kind, ?obligation, "assemble_unboxed_candidates");
match self.infcx.closure_kind(closure_substs) {
Some(closure_kind) => {
debug!(?closure_kind, "assemble_unboxed_candidates");
if closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate);
}
}
None => {
debug!("assemble_unboxed_candidates: closure_kind not yet known");
candidates.vec.push(ClosureCandidate);
}
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
candidates.ambiguous = true;
}
_ => {}
}
Ok(())
}
fn assemble_fn_pointer_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() {
return Ok(());
}
let self_ty = obligation.self_ty().skip_binder();
match *self_ty.kind() {
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_fn_pointer_candidates: ambiguous self-type");
candidates.ambiguous = true;
}
ty::FnPtr(_) => {
if let ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
c_variadic: false,
..
} = self_ty.fn_sig(self.tcx()).skip_binder()
{
candidates.vec.push(FnPointerCandidate);
}
}
ty::FnDef(def_id, _) => {
if let ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
c_variadic: false,
..
} = self_ty.fn_sig(self.tcx()).skip_binder()
{
if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() {
candidates.vec.push(FnPointerCandidate);
}
}
}
_ => {}
}
Ok(())
}
fn assemble_candidates_from_impls(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
debug!(?obligation, "assemble_candidates_from_impls");
if obligation.references_error() {
return Ok(());
}
self.tcx().for_each_relevant_impl(
obligation.predicate.def_id(),
obligation.predicate.skip_binder().trait_ref.self_ty(),
|impl_def_id| {
self.infcx.probe(|_| {
if let Ok(_substs) = self.match_impl(impl_def_id, obligation) {
candidates.vec.push(ImplCandidate(impl_def_id));
}
});
},
);
Ok(())
}
fn assemble_candidates_from_auto_impls(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
let self_ty = obligation.self_ty().skip_binder();
debug!(?self_ty, "assemble_candidates_from_auto_impls");
let def_id = obligation.predicate.def_id();
if self.tcx().trait_is_auto(def_id) {
match self_ty.kind() {
ty::Dynamic(..) => {
}
ty::Foreign(..) => {
}
ty::Param(..) | ty::Projection(..) => {
}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
}
ty::Generator(_, _, movability)
if self.tcx().lang_items().unpin_trait() == Some(def_id) =>
{
match movability {
hir::Movability::Static => {
}
hir::Movability::Movable => {
candidates.vec.push(BuiltinCandidate { has_nested: false });
}
}
}
_ => candidates.vec.push(AutoImplCandidate(def_id)),
}
}
Ok(())
}
fn assemble_candidates_from_object_ty(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
debug!(
self_ty = ?obligation.self_ty().skip_binder(),
"assemble_candidates_from_object_ty",
);
self.infcx.probe(|_snapshot| {
let self_ty = self.tcx().erase_late_bound_regions(obligation.self_ty());
let poly_trait_ref = match self_ty.kind() {
ty::Dynamic(ref data, ..) => {
if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
debug!(
"assemble_candidates_from_object_ty: matched builtin bound, \
pushing candidate"
);
candidates.vec.push(BuiltinObjectCandidate);
return;
}
if let Some(principal) = data.principal() {
if !self.infcx.tcx.features().object_safe_for_dispatch {
principal.with_self_ty(self.tcx(), self_ty)
} else if self.tcx().is_object_safe(principal.def_id()) {
principal.with_self_ty(self.tcx(), self_ty)
} else {
return;
}
} else {
return;
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_candidates_from_object_ty: ambiguous");
candidates.ambiguous = true;
return;
}
_ => return,
};
debug!(?poly_trait_ref, "assemble_candidates_from_object_ty");
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
self.infcx().replace_bound_vars_with_placeholders(poly_trait_predicate);
let candidate_supertraits = util::supertraits(self.tcx(), poly_trait_ref)
.enumerate()
.filter(|&(_, upcast_trait_ref)| {
self.infcx.probe(|_| {
self.match_normalize_trait_ref(
obligation,
upcast_trait_ref,
placeholder_trait_predicate.trait_ref,
)
.is_ok()
})
})
.map(|(idx, _)| ObjectCandidate(idx));
candidates.vec.extend(candidate_supertraits);
})
}
fn assemble_candidates_for_unsizing(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let source = match obligation.self_ty().no_bound_vars() {
Some(t) => t,
None => {
return;
}
};
let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
debug!(?source, ?target, "assemble_candidates_for_unsizing");
let may_apply = match (source.kind(), target.kind()) {
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
data_a.principal_def_id() == data_b.principal_def_id()
&& data_b
.auto_traits()
.all(|b| data_a.auto_traits().any(|a| a == b))
}
(_, &ty::Dynamic(..)) => true,
(&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => {
debug!("assemble_candidates_for_unsizing: ambiguous");
candidates.ambiguous = true;
false
}
(&ty::Array(..), &ty::Slice(_)) => true,
(&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => {
def_id_a == def_id_b
}
(&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(),
_ => false,
};
if may_apply {
candidates.vec.push(BuiltinUnsizeCandidate);
}
}
fn assemble_candidates_for_trait_alias(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
let self_ty = obligation.self_ty().skip_binder();
debug!(?self_ty, "assemble_candidates_for_trait_alias");
let def_id = obligation.predicate.def_id();
if self.tcx().is_trait_alias(def_id) {
candidates.vec.push(TraitAliasCandidate(def_id));
}
Ok(())
}
fn assemble_builtin_bound_candidates(
&mut self,
conditions: BuiltinImplConditions<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
match conditions {
BuiltinImplConditions::Where(nested) => {
debug!(?nested, "builtin_bound");
candidates
.vec
.push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() });
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
debug!("assemble_builtin_bound_candidates: ambiguous builtin");
candidates.ambiguous = true;
}
}
Ok(())
}
}