
Make proof tree probing and `Candidate`/`CandidateSource` generic over interner `<TyCtxt<'tcx>>` is ugly, but will become `<I>` when things actually become generic. r? lcnr
1185 lines
48 KiB
Rust
1185 lines
48 KiB
Rust
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
|
|
|
|
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
|
|
use super::assembly::{self, structural_traits, Candidate};
|
|
use super::{EvalCtxt, GoalSource, SolverMode};
|
|
use rustc_data_structures::fx::FxIndexSet;
|
|
use rustc_hir::def_id::DefId;
|
|
use rustc_hir::{LangItem, Movability};
|
|
use rustc_infer::infer::InferCtxt;
|
|
use rustc_infer::traits::query::NoSolution;
|
|
use rustc_infer::traits::solve::MaybeCause;
|
|
use rustc_middle::bug;
|
|
use rustc_middle::traits::solve::inspect::ProbeKind;
|
|
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult};
|
|
use rustc_middle::traits::{BuiltinImplSource, Reveal};
|
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
|
use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
|
|
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
|
|
use rustc_span::ErrorGuaranteed;
|
|
|
|
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|
fn self_ty(self) -> Ty<'tcx> {
|
|
self.self_ty()
|
|
}
|
|
|
|
fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
|
|
self.trait_ref
|
|
}
|
|
|
|
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
|
self.with_self_ty(tcx, self_ty)
|
|
}
|
|
|
|
fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
|
|
self.def_id()
|
|
}
|
|
|
|
fn consider_impl_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
|
impl_def_id: DefId,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let tcx = ecx.interner();
|
|
|
|
let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
|
|
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
|
|
if !drcx.args_may_unify(
|
|
goal.predicate.trait_ref.args,
|
|
impl_trait_header.trait_ref.skip_binder().args,
|
|
) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// An upper bound of the certainty of this goal, used to lower the certainty
|
|
// of reservation impl to ambiguous during coherence.
|
|
let impl_polarity = impl_trait_header.polarity;
|
|
let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
|
|
// In intercrate mode, this is ambiguous. But outside of intercrate,
|
|
// it's not a real impl.
|
|
(ty::ImplPolarity::Reservation, _) => match ecx.solver_mode() {
|
|
SolverMode::Coherence => Certainty::AMBIGUOUS,
|
|
SolverMode::Normal => return Err(NoSolution),
|
|
},
|
|
|
|
// Impl matches polarity
|
|
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
|
|
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes,
|
|
|
|
// Impl doesn't match polarity
|
|
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative)
|
|
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Positive) => {
|
|
return Err(NoSolution);
|
|
}
|
|
};
|
|
|
|
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
|
|
let impl_args = ecx.fresh_args_for_item(impl_def_id);
|
|
ecx.record_impl_args(impl_args);
|
|
let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args);
|
|
|
|
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
|
|
let where_clause_bounds = tcx
|
|
.predicates_of(impl_def_id)
|
|
.instantiate(tcx, impl_args)
|
|
.predicates
|
|
.into_iter()
|
|
.map(|pred| goal.with(tcx, pred));
|
|
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
|
|
|
|
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
|
})
|
|
}
|
|
|
|
fn consider_error_guaranteed_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
_guar: ErrorGuaranteed,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
// FIXME: don't need to enter a probe here.
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn probe_and_match_goal_against_assumption(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
source: CandidateSource<'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
assumption: ty::Clause<'tcx>,
|
|
then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, 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
|
|
{
|
|
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)
|
|
}
|
|
}
|
|
|
|
fn consider_auto_trait_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
|
|
return result;
|
|
}
|
|
|
|
// Don't call `type_of` on a local TAIT that's in the defining scope,
|
|
// since that may require calling `typeck` on the same item we're
|
|
// currently type checking, which will result in a fatal cycle that
|
|
// ideally we want to avoid, since we can make progress on this goal
|
|
// via an alias bound or a locally-inferred hidden type instead.
|
|
//
|
|
// Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since
|
|
// we already normalize the self type in
|
|
// `assemble_candidates_after_normalizing_self_ty`, and we'd
|
|
// just be registering an identical candidate here.
|
|
//
|
|
// We always return `Err(NoSolution)` here in `SolverMode::Coherence`
|
|
// since we'll always register an ambiguous candidate in
|
|
// `assemble_candidates_after_normalizing_self_ty` due to normalizing
|
|
// the TAIT.
|
|
if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
|
|
if matches!(goal.param_env.reveal(), Reveal::All)
|
|
|| matches!(ecx.solver_mode(), SolverMode::Coherence)
|
|
|| ecx.can_define_opaque_ty(opaque_ty.def_id)
|
|
{
|
|
return Err(NoSolution);
|
|
}
|
|
}
|
|
|
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
goal,
|
|
structural_traits::instantiate_constituent_tys_for_auto_trait,
|
|
)
|
|
}
|
|
|
|
fn consider_trait_alias_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tcx = ecx.interner();
|
|
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
|
let nested_obligations = tcx
|
|
.predicates_of(goal.predicate.def_id())
|
|
.instantiate(tcx, goal.predicate.trait_ref.args);
|
|
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
|
|
ecx.add_goals(
|
|
GoalSource::Misc,
|
|
nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)),
|
|
);
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
}
|
|
|
|
fn consider_builtin_sized_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
goal,
|
|
structural_traits::instantiate_constituent_tys_for_sized_trait,
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_copy_clone_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
goal,
|
|
structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_pointer_like_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// The regions of a type don't affect the size of the type
|
|
let tcx = ecx.interner();
|
|
// We should erase regions from both the param-env and type, since both
|
|
// may have infer regions. Specifically, after canonicalizing and instantiating,
|
|
// early bound regions turn into region vars in both the new and old solver.
|
|
let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty()));
|
|
// But if there are inference variables, we have to wait until it's resolved.
|
|
if key.has_non_region_infer() {
|
|
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
|
|
}
|
|
|
|
if let Ok(layout) = tcx.layout_of(key)
|
|
&& layout.layout.is_pointer_like(&tcx.data_layout)
|
|
{
|
|
// FIXME: We could make this faster by making a no-constraints response
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_fn_ptr_trait_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let self_ty = goal.predicate.self_ty();
|
|
match goal.predicate.polarity {
|
|
// impl FnPtr for FnPtr {}
|
|
ty::PredicatePolarity::Positive => {
|
|
if self_ty.is_fn_ptr() {
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
// impl !FnPtr for T where T != FnPtr && T is rigid {}
|
|
ty::PredicatePolarity::Negative => {
|
|
// If a type is rigid and not a fn ptr, then we know for certain
|
|
// that it does *not* implement `FnPtr`.
|
|
if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() {
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_fn_trait_candidates(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
goal_kind: ty::ClosureKind,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tcx = ecx.interner();
|
|
let tupled_inputs_and_output =
|
|
match structural_traits::extract_tupled_inputs_and_output_from_callable(
|
|
tcx,
|
|
goal.predicate.self_ty(),
|
|
goal_kind,
|
|
)? {
|
|
Some(a) => a,
|
|
None => {
|
|
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
|
|
}
|
|
};
|
|
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
|
|
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output])
|
|
});
|
|
|
|
let pred = tupled_inputs_and_output
|
|
.map_bound(|(inputs, _)| {
|
|
ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
|
})
|
|
.upcast(tcx);
|
|
// A built-in `Fn` impl only holds if the output is sized.
|
|
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
|
Self::probe_and_consider_implied_clause(
|
|
ecx,
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
goal,
|
|
pred,
|
|
[(GoalSource::ImplWhereBound, goal.with(tcx, output_is_sized_pred))],
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_async_fn_trait_candidates(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
goal_kind: ty::ClosureKind,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tcx = ecx.interner();
|
|
let (tupled_inputs_and_output_and_coroutine, nested_preds) =
|
|
structural_traits::extract_tupled_inputs_and_output_from_async_callable(
|
|
tcx,
|
|
goal.predicate.self_ty(),
|
|
goal_kind,
|
|
// This region doesn't matter because we're throwing away the coroutine type
|
|
tcx.lifetimes.re_static,
|
|
)?;
|
|
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
|
|AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
|
|
ty::TraitRef::new(
|
|
tcx,
|
|
tcx.require_lang_item(LangItem::Sized, None),
|
|
[output_coroutine_ty],
|
|
)
|
|
},
|
|
);
|
|
|
|
let pred = tupled_inputs_and_output_and_coroutine
|
|
.map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
|
|
ty::TraitRef::new(
|
|
tcx,
|
|
goal.predicate.def_id(),
|
|
[goal.predicate.self_ty(), tupled_inputs_ty],
|
|
)
|
|
})
|
|
.upcast(tcx);
|
|
// A built-in `AsyncFn` impl only holds if the output is sized.
|
|
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
|
Self::probe_and_consider_implied_clause(
|
|
ecx,
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
goal,
|
|
pred,
|
|
[goal.with(tcx, output_is_sized_pred)]
|
|
.into_iter()
|
|
.chain(nested_preds.into_iter().map(|pred| goal.with(tcx, pred)))
|
|
.map(|goal| (GoalSource::ImplWhereBound, goal)),
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_async_fn_kind_helper_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else {
|
|
bug!();
|
|
};
|
|
|
|
let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
|
|
// We don't need to worry about the self type being an infer var.
|
|
return Err(NoSolution);
|
|
};
|
|
let goal_kind = goal_kind_ty.expect_ty().to_opt_closure_kind().unwrap();
|
|
if closure_kind.extends(goal_kind) {
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
/// ```rust, ignore (not valid rust syntax)
|
|
/// impl Tuple for () {}
|
|
/// impl Tuple for (T1,) {}
|
|
/// impl Tuple for (T1, T2) {}
|
|
/// impl Tuple for (T1, .., Tn) {}
|
|
/// ```
|
|
fn consider_builtin_tuple_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_pointee_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_future_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// Coroutines are not futures unless they come from `async` desugaring
|
|
let tcx = ecx.interner();
|
|
if !tcx.coroutine_is_async(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// Async coroutine unconditionally implement `Future`
|
|
// Technically, we need to check that the future output type is Sized,
|
|
// but that's already proven by the coroutine being WF.
|
|
// FIXME: use `consider_implied`
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_iterator_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// Coroutines are not iterators unless they come from `gen` desugaring
|
|
let tcx = ecx.interner();
|
|
if !tcx.coroutine_is_gen(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// Gen coroutines unconditionally implement `Iterator`
|
|
// Technically, we need to check that the iterator output type is Sized,
|
|
// but that's already proven by the coroutines being WF.
|
|
// FIXME: use `consider_implied`
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_fused_iterator_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// Coroutines are not iterators unless they come from `gen` desugaring
|
|
let tcx = ecx.interner();
|
|
if !tcx.coroutine_is_gen(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// Gen coroutines unconditionally implement `FusedIterator`
|
|
// FIXME: use `consider_implied`
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_async_iterator_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// Coroutines are not iterators unless they come from `gen` desugaring
|
|
let tcx = ecx.interner();
|
|
if !tcx.coroutine_is_async_gen(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// Gen coroutines unconditionally implement `Iterator`
|
|
// Technically, we need to check that the iterator output type is Sized,
|
|
// but that's already proven by the coroutines being WF.
|
|
// FIXME: use `consider_implied`
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_coroutine_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let self_ty = goal.predicate.self_ty();
|
|
let ty::Coroutine(def_id, args) = *self_ty.kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// `async`-desugared coroutines do not implement the coroutine trait
|
|
let tcx = ecx.interner();
|
|
if !tcx.is_general_coroutine(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let coroutine = args.as_coroutine();
|
|
Self::probe_and_consider_implied_clause(
|
|
ecx,
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
goal,
|
|
ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
|
|
.upcast(tcx),
|
|
// Technically, we need to check that the coroutine types are Sized,
|
|
// but that's already proven by the coroutine being WF.
|
|
[],
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_discriminant_kind_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// `DiscriminantKind` is automatically implemented for every type.
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_async_destruct_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// `AsyncDestruct` is automatically implemented for every type.
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_destruct_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// FIXME(-Znext-solver): Implement this when we get const working in the new solver
|
|
|
|
// `Destruct` is automatically implemented for every type in
|
|
// non-const environments.
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
fn consider_builtin_transmute_candidate(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// `rustc_transmute` does not have support for type or const params
|
|
if goal.has_non_region_placeholders() {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// Erase regions because we compute layouts in `rustc_transmute`,
|
|
// which will ICE for region vars.
|
|
let args = ecx.interner().erase_regions(goal.predicate.trait_ref.args);
|
|
|
|
let Some(assume) =
|
|
rustc_transmute::Assume::from_const(ecx.interner(), goal.param_env, args.const_at(2))
|
|
else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// FIXME: This actually should destructure the `Result` we get from transmutability and
|
|
// register candiates. We probably need to register >1 since we may have an OR of ANDs.
|
|
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
|
let certainty = ecx.is_transmutable(
|
|
rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
|
|
assume,
|
|
)?;
|
|
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
|
})
|
|
}
|
|
|
|
/// ```ignore (builtin impl example)
|
|
/// trait Trait {
|
|
/// fn foo(&self);
|
|
/// }
|
|
/// // results in the following builtin impl
|
|
/// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
|
|
/// ```
|
|
fn consider_structural_builtin_unsize_candidates(
|
|
ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Vec<Candidate<TyCtxt<'tcx>>> {
|
|
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
|
return vec![];
|
|
}
|
|
|
|
let result_to_single = |result| match result {
|
|
Ok(resp) => vec![resp],
|
|
Err(NoSolution) => vec![],
|
|
};
|
|
|
|
ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
|
|
let a_ty = goal.predicate.self_ty();
|
|
// We need to normalize the b_ty since it's matched structurally
|
|
// in the other functions below.
|
|
let Ok(b_ty) = ecx.structurally_normalize_ty(
|
|
goal.param_env,
|
|
goal.predicate.trait_ref.args.type_at(1),
|
|
) else {
|
|
return vec![];
|
|
};
|
|
|
|
let goal = goal.with(ecx.interner(), (a_ty, b_ty));
|
|
match (a_ty.kind(), b_ty.kind()) {
|
|
(ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"),
|
|
|
|
(_, ty::Infer(ty::TyVar(..))) => {
|
|
result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity))
|
|
}
|
|
|
|
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
|
|
(
|
|
&ty::Dynamic(a_data, a_region, ty::Dyn),
|
|
&ty::Dynamic(b_data, b_region, ty::Dyn),
|
|
) => ecx.consider_builtin_dyn_upcast_candidates(
|
|
goal, a_data, a_region, b_data, b_region,
|
|
),
|
|
|
|
// `T` -> `dyn Trait` unsizing.
|
|
(_, &ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single(
|
|
ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data),
|
|
),
|
|
|
|
// `[T; N]` -> `[T]` unsizing
|
|
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
|
|
result_to_single(ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty))
|
|
}
|
|
|
|
// `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
|
|
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
|
|
if a_def.is_struct() && a_def == b_def =>
|
|
{
|
|
result_to_single(
|
|
ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args),
|
|
)
|
|
}
|
|
|
|
// `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>`
|
|
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
|
|
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
|
|
{
|
|
result_to_single(ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys))
|
|
}
|
|
|
|
_ => vec![],
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
|
|
/// Trait upcasting allows for coercions between trait objects:
|
|
/// ```ignore (builtin impl example)
|
|
/// trait Super {}
|
|
/// trait Trait: Super {}
|
|
/// // results in builtin impls upcasting to a super trait
|
|
/// impl<'a, 'b: 'a> Unsize<dyn Super + 'a> for dyn Trait + 'b {}
|
|
/// // and impls removing auto trait bounds.
|
|
/// impl<'a, 'b: 'a> Unsize<dyn Trait + 'a> for dyn Trait + Send + 'b {}
|
|
/// ```
|
|
fn consider_builtin_dyn_upcast_candidates(
|
|
&mut self,
|
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
|
a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
|
a_region: ty::Region<'tcx>,
|
|
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
|
b_region: ty::Region<'tcx>,
|
|
) -> Vec<Candidate<TyCtxt<'tcx>>> {
|
|
let tcx = self.interner();
|
|
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
|
|
|
|
let mut responses = vec![];
|
|
// If the principal def ids match (or are both none), then we're not doing
|
|
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
|
|
if a_data.principal_def_id() == b_data.principal_def_id() {
|
|
responses.extend(self.consider_builtin_upcast_to_principal(
|
|
goal,
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
|
a_data,
|
|
a_region,
|
|
b_data,
|
|
b_region,
|
|
a_data.principal(),
|
|
));
|
|
} else if let Some(a_principal) = a_data.principal() {
|
|
self.walk_vtable(
|
|
a_principal.with_self_ty(tcx, a_ty),
|
|
|ecx, new_a_principal, _, vtable_vptr_slot| {
|
|
responses.extend(ecx.consider_builtin_upcast_to_principal(
|
|
goal,
|
|
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting {
|
|
vtable_vptr_slot,
|
|
}),
|
|
a_data,
|
|
a_region,
|
|
b_data,
|
|
b_region,
|
|
Some(new_a_principal.map_bound(|trait_ref| {
|
|
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
|
})),
|
|
));
|
|
},
|
|
);
|
|
}
|
|
|
|
responses
|
|
}
|
|
|
|
fn consider_builtin_unsize_to_dyn_candidate(
|
|
&mut self,
|
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
|
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
|
b_region: ty::Region<'tcx>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let tcx = self.interner();
|
|
let Goal { predicate: (a_ty, _), .. } = goal;
|
|
|
|
// Can only unsize to an object-safe trait.
|
|
if b_data.principal_def_id().is_some_and(|def_id| !tcx.is_object_safe(def_id)) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
|
// Check that the type implements all of the predicates of the trait object.
|
|
// (i.e. the principal, all of the associated types match, and any auto traits)
|
|
ecx.add_goals(
|
|
GoalSource::ImplWhereBound,
|
|
b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
|
|
);
|
|
|
|
// The type must be `Sized` to be unsized.
|
|
ecx.add_goal(
|
|
GoalSource::ImplWhereBound,
|
|
goal.with(
|
|
tcx,
|
|
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [a_ty]),
|
|
),
|
|
);
|
|
|
|
// The type must outlive the lifetime of the `dyn` we're unsizing into.
|
|
ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
}
|
|
|
|
fn consider_builtin_upcast_to_principal(
|
|
&mut self,
|
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
|
source: CandidateSource<'tcx>,
|
|
a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
|
a_region: ty::Region<'tcx>,
|
|
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
|
b_region: ty::Region<'tcx>,
|
|
upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let param_env = goal.param_env;
|
|
|
|
// We may upcast to auto traits that are either explicitly listed in
|
|
// the object type's bounds, or implied by the principal trait ref's
|
|
// supertraits.
|
|
let a_auto_traits: FxIndexSet<DefId> = a_data
|
|
.auto_traits()
|
|
.chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
|
|
self.interner()
|
|
.supertrait_def_ids(principal_def_id)
|
|
.filter(|def_id| self.interner().trait_is_auto(*def_id))
|
|
}))
|
|
.collect();
|
|
|
|
// More than one projection in a_ty's bounds may match the projection
|
|
// in b_ty's bound. Use this to first determine *which* apply without
|
|
// having any inference side-effects. We process obligations because
|
|
// unification may initially succeed due to deferred projection equality.
|
|
let projection_may_match =
|
|
|ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
source_projection: ty::PolyExistentialProjection<'tcx>,
|
|
target_projection: ty::PolyExistentialProjection<'tcx>| {
|
|
source_projection.item_def_id() == target_projection.item_def_id()
|
|
&& ecx
|
|
.probe(|_| ProbeKind::UpcastProjectionCompatibility)
|
|
.enter(|ecx| -> Result<(), NoSolution> {
|
|
ecx.eq(param_env, source_projection, target_projection)?;
|
|
let _ = ecx.try_evaluate_added_goals()?;
|
|
Ok(())
|
|
})
|
|
.is_ok()
|
|
};
|
|
|
|
self.probe_trait_candidate(source).enter(|ecx| {
|
|
for bound in b_data {
|
|
match bound.skip_binder() {
|
|
// Check that a's supertrait (upcast_principal) is compatible
|
|
// with the target (b_ty).
|
|
ty::ExistentialPredicate::Trait(target_principal) => {
|
|
ecx.eq(
|
|
param_env,
|
|
upcast_principal.unwrap(),
|
|
bound.rebind(target_principal),
|
|
)?;
|
|
}
|
|
// Check that b_ty's projection is satisfied by exactly one of
|
|
// a_ty's projections. First, we look through the list to see if
|
|
// any match. If not, error. Then, if *more* than one matches, we
|
|
// return ambiguity. Otherwise, if exactly one matches, equate
|
|
// it with b_ty's projection.
|
|
ty::ExistentialPredicate::Projection(target_projection) => {
|
|
let target_projection = bound.rebind(target_projection);
|
|
let mut matching_projections =
|
|
a_data.projection_bounds().filter(|source_projection| {
|
|
projection_may_match(ecx, *source_projection, target_projection)
|
|
});
|
|
let Some(source_projection) = matching_projections.next() else {
|
|
return Err(NoSolution);
|
|
};
|
|
if matching_projections.next().is_some() {
|
|
return ecx.evaluate_added_goals_and_make_canonical_response(
|
|
Certainty::AMBIGUOUS,
|
|
);
|
|
}
|
|
ecx.eq(param_env, source_projection, target_projection)?;
|
|
}
|
|
// Check that b_ty's auto traits are present in a_ty's bounds.
|
|
ty::ExistentialPredicate::AutoTrait(def_id) => {
|
|
if !a_auto_traits.contains(&def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also require that a_ty's lifetime outlives b_ty's lifetime.
|
|
ecx.add_goal(
|
|
GoalSource::ImplWhereBound,
|
|
Goal::new(
|
|
ecx.interner(),
|
|
param_env,
|
|
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
|
|
),
|
|
);
|
|
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
}
|
|
|
|
/// We have the following builtin impls for arrays:
|
|
/// ```ignore (builtin impl example)
|
|
/// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {}
|
|
/// ```
|
|
/// While the impl itself could theoretically not be builtin,
|
|
/// the actual unsizing behavior is builtin. Its also easier to
|
|
/// make all impls of `Unsize` builtin as we're able to use
|
|
/// `#[rustc_deny_explicit_impl]` in this case.
|
|
fn consider_builtin_array_unsize(
|
|
&mut self,
|
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
|
a_elem_ty: Ty<'tcx>,
|
|
b_elem_ty: Ty<'tcx>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
|
|
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
/// We generate a builtin `Unsize` impls for structs with generic parameters only
|
|
/// mentioned by the last field.
|
|
/// ```ignore (builtin impl example)
|
|
/// struct Foo<T, U: ?Sized> {
|
|
/// sized_field: Vec<T>,
|
|
/// unsizable: Box<U>,
|
|
/// }
|
|
/// // results in the following builtin impl
|
|
/// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<Foo<T, V>> for Foo<T, U>
|
|
/// where
|
|
/// Box<U>: Unsize<Box<V>>,
|
|
/// {}
|
|
/// ```
|
|
fn consider_builtin_struct_unsize(
|
|
&mut self,
|
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
|
def: ty::AdtDef<'tcx>,
|
|
a_args: ty::GenericArgsRef<'tcx>,
|
|
b_args: ty::GenericArgsRef<'tcx>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let tcx = self.interner();
|
|
let Goal { predicate: (_a_ty, b_ty), .. } = goal;
|
|
|
|
let unsizing_params = tcx.unsizing_params_for_adt(def.did());
|
|
// We must be unsizing some type parameters. This also implies
|
|
// that the struct has a tail field.
|
|
if unsizing_params.is_empty() {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tail_field = def.non_enum_variant().tail();
|
|
let tail_field_ty = tcx.type_of(tail_field.did);
|
|
|
|
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
|
|
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
|
|
|
|
// Instantiate just the unsizing params from B into A. The type after
|
|
// this instantiation must be equal to B. This is so we don't unsize
|
|
// unrelated type parameters.
|
|
let new_a_args = tcx.mk_args_from_iter(
|
|
a_args
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
|
|
);
|
|
let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args);
|
|
|
|
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
|
|
// types.
|
|
self.eq(goal.param_env, unsized_a_ty, b_ty)?;
|
|
self.add_goal(
|
|
GoalSource::ImplWhereBound,
|
|
goal.with(
|
|
tcx,
|
|
ty::TraitRef::new(
|
|
tcx,
|
|
tcx.require_lang_item(LangItem::Unsize, None),
|
|
[a_tail_ty, b_tail_ty],
|
|
),
|
|
),
|
|
);
|
|
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
/// We generate the following builtin impl for tuples of all sizes.
|
|
///
|
|
/// This impl is still unstable and we emit a feature error when it
|
|
/// when it is used by a coercion.
|
|
/// ```ignore (builtin impl example)
|
|
/// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<(T, V)> for (T, U)
|
|
/// where
|
|
/// U: Unsize<V>,
|
|
/// {}
|
|
/// ```
|
|
fn consider_builtin_tuple_unsize(
|
|
&mut self,
|
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
|
a_tys: &'tcx ty::List<Ty<'tcx>>,
|
|
b_tys: &'tcx ty::List<Ty<'tcx>>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
let tcx = self.interner();
|
|
let Goal { predicate: (_a_ty, b_ty), .. } = goal;
|
|
|
|
let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
|
|
let &b_last_ty = b_tys.last().unwrap();
|
|
|
|
// Instantiate just the tail field of B., and require that they're equal.
|
|
let unsized_a_ty =
|
|
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty]));
|
|
self.eq(goal.param_env, unsized_a_ty, b_ty)?;
|
|
|
|
// Similar to ADTs, require that we can unsize the tail.
|
|
self.add_goal(
|
|
GoalSource::ImplWhereBound,
|
|
goal.with(
|
|
tcx,
|
|
ty::TraitRef::new(
|
|
tcx,
|
|
tcx.require_lang_item(LangItem::Unsize, None),
|
|
[a_last_ty, b_last_ty],
|
|
),
|
|
),
|
|
);
|
|
self.probe_builtin_trait_candidate(BuiltinImplSource::TupleUnsizing)
|
|
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
|
|
// Return `Some` if there is an impl (built-in or user provided) that may
|
|
// hold for the self type of the goal, which for coherence and soundness
|
|
// purposes must disqualify the built-in auto impl assembled by considering
|
|
// the type's constituent types.
|
|
fn disqualify_auto_trait_candidate_due_to_possible_impl(
|
|
&mut self,
|
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
|
) -> Option<Result<Candidate<TyCtxt<'tcx>>, NoSolution>> {
|
|
let self_ty = goal.predicate.self_ty();
|
|
match *self_ty.kind() {
|
|
// Stall int and float vars until they are resolved to a concrete
|
|
// numerical type. That's because the check for impls below treats
|
|
// int vars as matching any impl. Even if we filtered such impls,
|
|
// we probably don't want to treat an `impl !AutoTrait for i32` as
|
|
// disqualifying the built-in auto impl for `i64: AutoTrait` either.
|
|
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
|
|
Some(self.forced_ambiguity(MaybeCause::Ambiguity))
|
|
}
|
|
|
|
// These types cannot be structurally decomposed into constituent
|
|
// types, and therefore have no built-in auto impl.
|
|
ty::Dynamic(..)
|
|
| ty::Param(..)
|
|
| ty::Foreign(..)
|
|
| ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..)
|
|
| ty::Placeholder(..) => Some(Err(NoSolution)),
|
|
|
|
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
|
|
|
|
// Coroutines have one special built-in candidate, `Unpin`, which
|
|
// takes precedence over the structural auto trait candidate being
|
|
// assembled.
|
|
ty::Coroutine(def_id, _)
|
|
if self.interner().is_lang_item(goal.predicate.def_id(), LangItem::Unpin) =>
|
|
{
|
|
match self.interner().coroutine_movability(def_id) {
|
|
Movability::Static => Some(Err(NoSolution)),
|
|
Movability::Movable => Some(
|
|
self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}),
|
|
),
|
|
}
|
|
}
|
|
|
|
// If we still have an alias here, it must be rigid. For opaques, it's always
|
|
// okay to consider auto traits because that'll reveal its hidden type. For
|
|
// non-opaque aliases, we will not assemble any candidates since there's no way
|
|
// to further look into its type.
|
|
ty::Alias(..) => None,
|
|
|
|
// For rigid types, any possible implementation that could apply to
|
|
// the type (even if after unification and processing nested goals
|
|
// it does not hold) will disqualify the built-in auto impl.
|
|
//
|
|
// This differs from the current stable behavior and fixes #84857.
|
|
// Due to breakage found via crater, we currently instead lint
|
|
// patterns which can be used to exploit this unsoundness on stable,
|
|
// see #93367 for more details.
|
|
ty::Bool
|
|
| ty::Char
|
|
| ty::Int(_)
|
|
| ty::Uint(_)
|
|
| ty::Float(_)
|
|
| ty::Str
|
|
| ty::Array(_, _)
|
|
| ty::Pat(_, _)
|
|
| ty::Slice(_)
|
|
| ty::RawPtr(_, _)
|
|
| ty::Ref(_, _, _)
|
|
| ty::FnDef(_, _)
|
|
| ty::FnPtr(_)
|
|
| ty::Closure(..)
|
|
| ty::CoroutineClosure(..)
|
|
| ty::Coroutine(_, _)
|
|
| ty::CoroutineWitness(..)
|
|
| ty::Never
|
|
| ty::Tuple(_)
|
|
| ty::Adt(_, _) => {
|
|
let mut disqualifying_impl = None;
|
|
self.interner().for_each_relevant_impl(
|
|
goal.predicate.def_id(),
|
|
goal.predicate.self_ty(),
|
|
|impl_def_id| {
|
|
disqualifying_impl = Some(impl_def_id);
|
|
},
|
|
);
|
|
if let Some(def_id) = disqualifying_impl {
|
|
trace!(?def_id, ?goal, "disqualified auto-trait implementation");
|
|
// No need to actually consider the candidate here,
|
|
// since we do that in `consider_impl_candidate`.
|
|
return Some(Err(NoSolution));
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
ty::Error(_) => None,
|
|
}
|
|
}
|
|
|
|
/// Convenience function for traits that are structural, i.e. that only
|
|
/// have nested subgoals that only change the self type. Unlike other
|
|
/// evaluate-like helpers, this does a probe, so it doesn't need to be
|
|
/// wrapped in one.
|
|
fn probe_and_evaluate_goal_for_constituent_tys(
|
|
&mut self,
|
|
source: CandidateSource<'tcx>,
|
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
|
constituent_tys: impl Fn(
|
|
&EvalCtxt<'_, InferCtxt<'tcx>>,
|
|
Ty<'tcx>,
|
|
) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution>,
|
|
) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
|
|
self.probe_trait_candidate(source).enter(|ecx| {
|
|
ecx.add_goals(
|
|
GoalSource::ImplWhereBound,
|
|
constituent_tys(ecx, goal.predicate.self_ty())?
|
|
.into_iter()
|
|
.map(|ty| {
|
|
ecx.enter_forall(ty, |ty| {
|
|
goal.with(
|
|
ecx.interner(),
|
|
goal.predicate.with_self_ty(ecx.interner(), ty),
|
|
)
|
|
})
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
);
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
}
|
|
|
|
#[instrument(level = "trace", skip(self))]
|
|
pub(super) fn compute_trait_goal(
|
|
&mut self,
|
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
|
) -> QueryResult<'tcx> {
|
|
let candidates = self.assemble_and_evaluate_candidates(goal);
|
|
self.merge_candidates(candidates)
|
|
}
|
|
}
|