use rustc_hir as hir;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_target::spec::abi::Abi;
use crate::traits::{util, SelectionResult};
use super::BuiltinImplConditions;
use super::SelectionCandidate::{self, *};
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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!(
"candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})",
cache_fresh_trait_pred, stack
);
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!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c);
return c;
}
let (candidate, dep_node) =
self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate);
self.insert_candidate_cache(
stack.obligation.param_env,
cache_fresh_trait_pred,
dep_node,
candidate.clone(),
);
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 is {:?}", 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!("assemble_candidates_for_projected_tys({:?})", obligation);
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));
if result {
candidates.vec.push(ProjectionCandidate);
}
}
fn assemble_candidates_from_caller_bounds<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation);
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());
let mut param_candidates = vec![];
for bound in matching_bounds {
let wc = self.evaluate_where_clause(stack, bound)?;
if wc.may_apply() {
param_candidates.push(ParamCandidate(bound));
}
}
candidates.vec.extend(param_candidates);
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!(
"assemble_generator_candidates: self_ty={:?} obligation={:?}",
self_ty, obligation
);
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!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation);
match self.infcx.closure_kind(closure_substs) {
Some(closure_kind) => {
debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
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!("assemble_candidates_from_impls(obligation={:?})", obligation);
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!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty);
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!(
"assemble_candidates_from_object_ty(self_ty={:?})",
obligation.self_ty().skip_binder()
);
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!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref);
let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
.filter(|upcast_trait_ref| {
self.infcx
.probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok())
})
.count();
if upcast_trait_refs > 1 {
candidates.ambiguous = true;
} else if upcast_trait_refs == 1 {
candidates.vec.push(ObjectCandidate);
}
})
}
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!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target);
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!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty);
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!("builtin_bound: nested={:?}", nested);
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(())
}
}