
Add `AliasKind::Weak` for type aliases. `type Foo<T: Debug> = Bar<T>;` does not check `T: Debug` at use sites of `Foo<NotDebug>`, because in contrast to a ```rust trait Identity { type Identity; } impl<T: Debug> Identity for T { type Identity = T; } <NotDebug as Identity>::Identity ``` type aliases do not exist in the type system, but are expanded to their aliased type immediately when going from HIR to the type layer. Similarly: * a private type alias for a public type is a completely fine thing, even though it makes it a bit hard to write out complex times sometimes * rustdoc expands the type alias, even though often times users use them for documentation purposes * diagnostics show the expanded type, which is confusing if the user wrote a type alias and the diagnostic talks about another type that they don't know about. For type alias impl trait, these issues do not actually apply in most cases, but sometimes you have a type alias impl trait like `type Foo<T: Debug> = (impl Debug, Bar<T>);`, which only really checks it for `impl Debug`, but by accident prevents `Bar<T>` from only being instantiated after proving `T: Debug`. This PR makes sure that we always check these bounds explicitly and don't rely on an implementation accident. To not break all the type aliases out there, we only use it when the type alias contains an opaque type. We can decide to do this for all type aliases over an edition. Or we can later extend this to more types if we figure out the back-compat concerns with suddenly checking such bounds. As a side effect, easily allows fixing https://github.com/rust-lang/rust/issues/108617, which I did. fixes https://github.com/rust-lang/rust/issues/108617
725 lines
29 KiB
Rust
725 lines
29 KiB
Rust
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
|
|
|
|
use super::assembly::{self, structural_traits};
|
|
use super::{EvalCtxt, SolverMode};
|
|
use rustc_hir::def_id::DefId;
|
|
use rustc_hir::{LangItem, Movability};
|
|
use rustc_infer::traits::query::NoSolution;
|
|
use rustc_infer::traits::util::supertraits;
|
|
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
|
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
|
|
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
|
|
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
|
|
use rustc_span::DUMMY_SP;
|
|
|
|
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<'_, 'tcx>,
|
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
|
impl_def_id: DefId,
|
|
) -> QueryResult<'tcx> {
|
|
let tcx = ecx.tcx();
|
|
|
|
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
|
|
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
|
|
if !drcx.substs_refs_may_unify(
|
|
goal.predicate.trait_ref.substs,
|
|
impl_trait_ref.skip_binder().substs,
|
|
) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let impl_polarity = tcx.impl_polarity(impl_def_id);
|
|
// An upper bound of the certainty of this goal, used to lower the certainty
|
|
// of reservation impl to ambiguous during coherence.
|
|
let maximal_certainty = match impl_polarity {
|
|
ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => {
|
|
match impl_polarity == goal.predicate.polarity {
|
|
true => Certainty::Yes,
|
|
false => return Err(NoSolution),
|
|
}
|
|
}
|
|
ty::ImplPolarity::Reservation => match ecx.solver_mode() {
|
|
SolverMode::Normal => return Err(NoSolution),
|
|
SolverMode::Coherence => Certainty::AMBIGUOUS,
|
|
},
|
|
};
|
|
|
|
ecx.probe(|ecx| {
|
|
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
|
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
|
|
|
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_substs)
|
|
.predicates
|
|
.into_iter()
|
|
.map(|pred| goal.with(tcx, pred));
|
|
ecx.add_goals(where_clause_bounds);
|
|
|
|
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
|
})
|
|
}
|
|
|
|
fn probe_and_match_goal_against_assumption(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
assumption: ty::Binder<'tcx, ty::Clause<'tcx>>,
|
|
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
|
|
) -> QueryResult<'tcx> {
|
|
if let Some(trait_clause) = assumption.as_trait_clause()
|
|
&& trait_clause.def_id() == goal.predicate.def_id()
|
|
&& trait_clause.polarity() == goal.predicate.polarity
|
|
{
|
|
// FIXME: Constness
|
|
ecx.probe(|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)
|
|
}
|
|
}
|
|
|
|
fn consider_auto_trait_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
|
|
return result;
|
|
}
|
|
|
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
|
goal,
|
|
structural_traits::instantiate_constituent_tys_for_auto_trait,
|
|
)
|
|
}
|
|
|
|
fn consider_trait_alias_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tcx = ecx.tcx();
|
|
|
|
ecx.probe(|ecx| {
|
|
let nested_obligations = tcx
|
|
.predicates_of(goal.predicate.def_id())
|
|
.instantiate(tcx, goal.predicate.trait_ref.substs);
|
|
ecx.add_goals(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<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
|
goal,
|
|
structural_traits::instantiate_constituent_tys_for_sized_trait,
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_copy_clone_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
|
goal,
|
|
structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_pointer_like_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// The regions of a type don't affect the size of the type
|
|
let tcx = ecx.tcx();
|
|
// 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.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
|
}
|
|
|
|
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.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_fn_ptr_trait_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
if let ty::FnPtr(..) = goal.predicate.self_ty().kind() {
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_fn_trait_candidates(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
goal_kind: ty::ClosureKind,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tcx = ecx.tcx();
|
|
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
|
|
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
|
}
|
|
};
|
|
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
|
|
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
|
|
});
|
|
|
|
let pred = tupled_inputs_and_output
|
|
.map_bound(|(inputs, _)| {
|
|
ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
|
})
|
|
.to_predicate(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::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
|
|
}
|
|
|
|
fn consider_builtin_tuple_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
} else {
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_pointee_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
|
|
fn consider_builtin_future_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// Generators are not futures unless they come from `async` desugaring
|
|
let tcx = ecx.tcx();
|
|
if !tcx.generator_is_async(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// Async generator unconditionally implement `Future`
|
|
// Technically, we need to check that the future output type is Sized,
|
|
// but that's already proven by the generator being WF.
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
|
|
fn consider_builtin_generator_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let self_ty = goal.predicate.self_ty();
|
|
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
// `async`-desugared generators do not implement the generator trait
|
|
let tcx = ecx.tcx();
|
|
if tcx.generator_is_async(def_id) {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let generator = substs.as_generator();
|
|
Self::consider_implied_clause(
|
|
ecx,
|
|
goal,
|
|
ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, generator.resume_ty()])
|
|
.to_predicate(tcx),
|
|
// Technically, we need to check that the generator types are Sized,
|
|
// but that's already proven by the generator being WF.
|
|
[],
|
|
)
|
|
}
|
|
|
|
fn consider_builtin_unsize_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let tcx = ecx.tcx();
|
|
let a_ty = goal.predicate.self_ty();
|
|
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
|
|
if b_ty.is_ty_var() {
|
|
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
|
}
|
|
ecx.probe(|ecx| {
|
|
match (a_ty.kind(), b_ty.kind()) {
|
|
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
|
|
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
|
|
// Dyn upcasting is handled separately, since due to upcasting,
|
|
// when there are two supertraits that differ by substs, we
|
|
// may return more than one query response.
|
|
Err(NoSolution)
|
|
}
|
|
// `T` -> `dyn Trait` unsizing
|
|
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
|
|
// Can only unsize to an object-safe type
|
|
if data
|
|
.principal_def_id()
|
|
.is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
|
|
{
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
|
|
return Err(NoSolution);
|
|
};
|
|
// Check that the type implements all of the predicates of the def-id.
|
|
// (i.e. the principal, all of the associated types match, and any auto traits)
|
|
ecx.add_goals(
|
|
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(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
|
|
// The type must outlive the lifetime of the `dyn` we're unsizing into.
|
|
ecx.add_goal(
|
|
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
|
|
);
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
// `[T; n]` -> `[T]` unsizing
|
|
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
|
|
// We just require that the element type stays the same
|
|
ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
|
|
(&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
|
|
if a_def.is_struct() && a_def.did() == b_def.did() =>
|
|
{
|
|
let unsizing_params = tcx.unsizing_params_for_adt(a_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 = a_def
|
|
.non_enum_variant()
|
|
.fields
|
|
.raw
|
|
.last()
|
|
.expect("expected unsized ADT to have a tail field");
|
|
let tail_field_ty = tcx.type_of(tail_field.did);
|
|
|
|
let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
|
|
let b_tail_ty = tail_field_ty.subst(tcx, b_substs);
|
|
|
|
// Substitute just the unsizing params from B into A. The type after
|
|
// this substitution must be equal to B. This is so we don't unsize
|
|
// unrelated type parameters.
|
|
let new_a_substs =
|
|
tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
|
|
if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
|
|
}));
|
|
let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs);
|
|
|
|
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
|
|
// types.
|
|
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
|
|
ecx.add_goal(goal.with(
|
|
tcx,
|
|
ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
|
|
));
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
|
|
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
|
|
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
|
|
{
|
|
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
|
|
let b_last_ty = b_tys.last().unwrap();
|
|
|
|
// Substitute just the tail field of B., and require that they're equal.
|
|
let unsized_a_ty =
|
|
tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied());
|
|
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
|
|
|
|
// Similar to ADTs, require that the rest of the fields are equal.
|
|
ecx.add_goal(goal.with(
|
|
tcx,
|
|
ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
|
|
));
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
_ => Err(NoSolution),
|
|
}
|
|
})
|
|
}
|
|
|
|
fn consider_builtin_dyn_upcast_candidates(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> Vec<CanonicalResponse<'tcx>> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return vec![];
|
|
}
|
|
|
|
let tcx = ecx.tcx();
|
|
|
|
let a_ty = goal.predicate.self_ty();
|
|
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
|
|
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
|
|
return vec![];
|
|
};
|
|
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
|
|
return vec![];
|
|
};
|
|
|
|
// All of a's auto traits need to be in b's auto traits.
|
|
let auto_traits_compatible =
|
|
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
|
|
if !auto_traits_compatible {
|
|
return vec![];
|
|
}
|
|
|
|
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
|
|
ecx.probe(|ecx| -> Result<_, NoSolution> {
|
|
// Require that all of the trait predicates from A match B, except for
|
|
// the auto traits. We do this by constructing a new A type with B's
|
|
// auto traits, and equating these types.
|
|
let new_a_data = principal
|
|
.into_iter()
|
|
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
|
|
.chain(a_data.iter().filter(|a| {
|
|
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
|
|
}))
|
|
.chain(
|
|
b_data
|
|
.auto_traits()
|
|
.map(ty::ExistentialPredicate::AutoTrait)
|
|
.map(ty::Binder::dummy),
|
|
);
|
|
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
|
|
let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
|
|
|
|
// We also require that A's lifetime outlives B's lifetime.
|
|
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
|
|
ecx.add_goal(
|
|
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
|
|
);
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
};
|
|
|
|
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() {
|
|
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
|
|
responses.push(response);
|
|
}
|
|
} else if let Some(a_principal) = a_data.principal()
|
|
&& let Some(b_principal) = b_data.principal()
|
|
{
|
|
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
|
|
if super_trait_ref.def_id() != b_principal.def_id() {
|
|
continue;
|
|
}
|
|
let erased_trait_ref = super_trait_ref
|
|
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
|
|
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
|
|
responses.push(response);
|
|
}
|
|
}
|
|
}
|
|
|
|
responses
|
|
}
|
|
|
|
fn consider_builtin_discriminant_kind_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
// `DiscriminantKind` is automatically implemented for every type.
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
}
|
|
|
|
fn consider_builtin_destruct_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
|
return Err(NoSolution);
|
|
}
|
|
|
|
if !goal.param_env.is_const() {
|
|
// `Destruct` is automatically implemented for every type in
|
|
// non-const environments.
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
} else {
|
|
// FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver
|
|
Err(NoSolution)
|
|
}
|
|
}
|
|
|
|
fn consider_builtin_transmute_candidate(
|
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
|
goal: Goal<'tcx, Self>,
|
|
) -> QueryResult<'tcx> {
|
|
if goal.predicate.polarity != ty::ImplPolarity::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 substs = ecx.tcx().erase_regions(goal.predicate.trait_ref.substs);
|
|
|
|
let Some(assume) = rustc_transmute::Assume::from_const(
|
|
ecx.tcx(),
|
|
goal.param_env,
|
|
substs.const_at(3),
|
|
) else {
|
|
return Err(NoSolution);
|
|
};
|
|
|
|
let certainty = ecx.is_transmutable(
|
|
rustc_transmute::Types { dst: substs.type_at(0), src: substs.type_at(1) },
|
|
substs.type_at(2),
|
|
assume,
|
|
)?;
|
|
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
|
}
|
|
}
|
|
|
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|
// 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<QueryResult<'tcx>> {
|
|
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.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS))
|
|
}
|
|
|
|
// 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}`"),
|
|
|
|
// Generators have one special built-in candidate, `Unpin`, which
|
|
// takes precedence over the structural auto trait candidate being
|
|
// assembled.
|
|
ty::Generator(_, _, movability)
|
|
if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() =>
|
|
{
|
|
match movability {
|
|
Movability::Static => Some(Err(NoSolution)),
|
|
Movability::Movable => {
|
|
Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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::Slice(_)
|
|
| ty::RawPtr(_)
|
|
| ty::Ref(_, _, _)
|
|
| ty::FnDef(_, _)
|
|
| ty::FnPtr(_)
|
|
| ty::Closure(_, _)
|
|
| ty::Generator(_, _, _)
|
|
| ty::GeneratorWitness(_)
|
|
| ty::GeneratorWitnessMIR(_, _)
|
|
| ty::Never
|
|
| ty::Tuple(_)
|
|
| ty::Adt(_, _)
|
|
// FIXME: Handling opaques here is kinda sus. Especially because we
|
|
// simplify them to PlaceholderSimplifiedType.
|
|
| ty::Alias(ty::Opaque, _) => {
|
|
let mut disqualifying_impl = None;
|
|
self.tcx().for_each_relevant_impl_treating_projections(
|
|
goal.predicate.def_id(),
|
|
goal.predicate.self_ty(),
|
|
TreatProjections::NextSolverLookup,
|
|
|impl_def_id| {
|
|
disqualifying_impl = Some(impl_def_id);
|
|
},
|
|
);
|
|
if let Some(def_id) = disqualifying_impl {
|
|
debug!(?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,
|
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
|
constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
|
|
) -> QueryResult<'tcx> {
|
|
self.probe(|ecx| {
|
|
ecx.add_goals(
|
|
constituent_tys(ecx, goal.predicate.self_ty())?
|
|
.into_iter()
|
|
.map(|ty| {
|
|
goal.with(
|
|
ecx.tcx(),
|
|
ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)),
|
|
)
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
);
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
})
|
|
}
|
|
|
|
#[instrument(level = "debug", 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)
|
|
}
|
|
}
|