Normalize the RHS of an unsize goal
This commit is contained in:
parent
23405bb123
commit
7e66c0b7ed
5 changed files with 169 additions and 69 deletions
|
@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> {
|
||||||
pub enum CandidateKind<'tcx> {
|
pub enum CandidateKind<'tcx> {
|
||||||
/// Probe entered when normalizing the self ty during candidate assembly
|
/// Probe entered when normalizing the self ty during candidate assembly
|
||||||
NormalizedSelfTyAssembly,
|
NormalizedSelfTyAssembly,
|
||||||
|
DynUpcastingAssembly,
|
||||||
/// A normal candidate for proving a goal
|
/// A normal candidate for proving a goal
|
||||||
Candidate { name: String, result: QueryResult<'tcx> },
|
Candidate {
|
||||||
|
name: String,
|
||||||
|
result: QueryResult<'tcx>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
impl Debug for GoalCandidate<'_> {
|
impl Debug for GoalCandidate<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
|
@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||||
CandidateKind::NormalizedSelfTyAssembly => {
|
CandidateKind::NormalizedSelfTyAssembly => {
|
||||||
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
||||||
}
|
}
|
||||||
|
CandidateKind::DynUpcastingAssembly => {
|
||||||
|
writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:")
|
||||||
|
}
|
||||||
CandidateKind::Candidate { name, result } => {
|
CandidateKind::Candidate { name, result } => {
|
||||||
writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
|
writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,9 +330,13 @@ fn rematch_unsize<'tcx>(
|
||||||
mut nested: Vec<PredicateObligation<'tcx>>,
|
mut nested: Vec<PredicateObligation<'tcx>>,
|
||||||
) -> SelectionResult<'tcx, Selection<'tcx>> {
|
) -> SelectionResult<'tcx, Selection<'tcx>> {
|
||||||
let tcx = infcx.tcx;
|
let tcx = infcx.tcx;
|
||||||
let a_ty = goal.predicate.self_ty();
|
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
|
||||||
let b_ty = goal.predicate.trait_ref.args.type_at(1);
|
let b_ty = structurally_normalize(
|
||||||
|
goal.predicate.trait_ref.args.type_at(1),
|
||||||
|
infcx,
|
||||||
|
goal.param_env,
|
||||||
|
&mut nested,
|
||||||
|
);
|
||||||
match (a_ty.kind(), b_ty.kind()) {
|
match (a_ty.kind(), b_ty.kind()) {
|
||||||
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
|
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
|
||||||
// Check that the type implements all of the predicates of the def-id.
|
// Check that the type implements all of the predicates of the def-id.
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
|
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
|
||||||
|
|
||||||
use super::assembly::{self, structural_traits};
|
use super::assembly::{self, structural_traits};
|
||||||
|
use super::search_graph::OverflowHandler;
|
||||||
use super::{EvalCtxt, SolverMode};
|
use super::{EvalCtxt, SolverMode};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{LangItem, Movability};
|
use rustc_hir::{LangItem, Movability};
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::util::supertraits;
|
use rustc_infer::traits::util::supertraits;
|
||||||
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
|
use rustc_middle::traits::solve::inspect::CandidateKind;
|
||||||
|
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
|
||||||
use rustc_middle::traits::Reveal;
|
use rustc_middle::traits::Reveal;
|
||||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
|
||||||
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
|
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
|
||||||
|
@ -376,11 +378,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
let tcx = ecx.tcx();
|
let tcx = ecx.tcx();
|
||||||
let a_ty = goal.predicate.self_ty();
|
let a_ty = goal.predicate.self_ty();
|
||||||
let b_ty = goal.predicate.trait_ref.args.type_at(1);
|
let b_ty = goal.predicate.trait_ref.args.type_at(1);
|
||||||
if b_ty.is_ty_var() {
|
|
||||||
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
|
||||||
}
|
|
||||||
ecx.probe_candidate("builtin unsize").enter(|ecx| {
|
ecx.probe_candidate("builtin unsize").enter(|ecx| {
|
||||||
|
let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else {
|
||||||
|
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
|
||||||
|
MaybeCause::Overflow,
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
match (a_ty.kind(), b_ty.kind()) {
|
match (a_ty.kind(), b_ty.kind()) {
|
||||||
|
(_, ty::Infer(ty::TyVar(_))) => {
|
||||||
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||||
|
}
|
||||||
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
|
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
|
||||||
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
|
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
|
||||||
// Dyn upcasting is handled separately, since due to upcasting,
|
// Dyn upcasting is handled separately, since due to upcasting,
|
||||||
|
@ -489,11 +498,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
|
|
||||||
let tcx = ecx.tcx();
|
let tcx = ecx.tcx();
|
||||||
|
|
||||||
|
// Need to wrap in a probe since `normalize_non_self_ty` has side-effects.
|
||||||
|
ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| {
|
||||||
let a_ty = goal.predicate.self_ty();
|
let a_ty = goal.predicate.self_ty();
|
||||||
let b_ty = goal.predicate.trait_ref.args.type_at(1);
|
let b_ty = goal.predicate.trait_ref.args.type_at(1);
|
||||||
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
|
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
|
||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We don't care about `ty::Infer` here or errors here, since we'll
|
||||||
|
// register an ambiguous/error response in the other unsize candidate
|
||||||
|
// assembly function.
|
||||||
|
let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
|
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
|
||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
|
@ -505,8 +523,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
|
let mut unsize_dyn_to_principal = |principal: Option<
|
||||||
ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
|
ty::PolyExistentialTraitRef<'tcx>,
|
||||||
|
>| {
|
||||||
|
ecx.probe_candidate("upcast dyn to principle").enter(
|
||||||
|
|ecx| -> Result<_, NoSolution> {
|
||||||
// Require that all of the trait predicates from A match B, except for
|
// 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
|
// the auto traits. We do this by constructing a new A type with B's
|
||||||
// auto traits, and equating these types.
|
// auto traits, and equating these types.
|
||||||
|
@ -527,11 +548,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
|
|
||||||
// We also require that A's lifetime outlives B's lifetime.
|
// We also require that A's lifetime outlives B's lifetime.
|
||||||
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
|
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
|
||||||
ecx.add_goal(
|
ecx.add_goal(goal.with(
|
||||||
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
|
tcx,
|
||||||
);
|
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
|
||||||
|
));
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut responses = vec![];
|
let mut responses = vec![];
|
||||||
|
@ -548,8 +571,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
if super_trait_ref.def_id() != b_principal.def_id() {
|
if super_trait_ref.def_id() != b_principal.def_id() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let erased_trait_ref = super_trait_ref
|
let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| {
|
||||||
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
|
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||||
|
});
|
||||||
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
|
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
|
||||||
responses.push(response);
|
responses.push(response);
|
||||||
}
|
}
|
||||||
|
@ -557,6 +581,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
responses
|
responses
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_discriminant_kind_candidate(
|
fn consider_builtin_discriminant_kind_candidate(
|
||||||
|
@ -750,4 +775,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||||
self.merge_candidates(candidates)
|
self.merge_candidates(candidates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize a non-self type when it is structually matched on when solving
|
||||||
|
/// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty`
|
||||||
|
/// for the self type, but for other goals, additional normalization of other
|
||||||
|
/// arguments may be needed to completely implement the semantics of the trait.
|
||||||
|
///
|
||||||
|
/// This is required when structurally matching on any trait argument that is
|
||||||
|
/// not the self type.
|
||||||
|
fn normalize_non_self_ty(
|
||||||
|
&mut self,
|
||||||
|
mut ty: Ty<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
) -> Result<Option<Ty<'tcx>>, NoSolution> {
|
||||||
|
if !matches!(ty.kind(), ty::Alias(..)) {
|
||||||
|
return Ok(Some(ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.repeat_while_none(
|
||||||
|
|_| Ok(None),
|
||||||
|
|ecx| {
|
||||||
|
let ty::Alias(_, projection_ty) = *ty.kind() else {
|
||||||
|
return Some(Ok(Some(ty)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let normalized_ty = ecx.next_ty_infer();
|
||||||
|
let normalizes_to_goal = Goal::new(
|
||||||
|
ecx.tcx(),
|
||||||
|
param_env,
|
||||||
|
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||||
|
projection_ty,
|
||||||
|
term: normalized_ty.into(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
ecx.add_goal(normalizes_to_goal);
|
||||||
|
if let Err(err) = ecx.try_evaluate_added_goals() {
|
||||||
|
return Some(Err(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
ty = ecx.resolve_vars_if_possible(normalized_ty);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
21
tests/ui/traits/new-solver/normalize-unsize-rhs.rs
Normal file
21
tests/ui/traits/new-solver/normalize-unsize-rhs.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// compile-flags: -Ztrait-solver=next
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
trait A {}
|
||||||
|
trait B: A {}
|
||||||
|
|
||||||
|
impl A for usize {}
|
||||||
|
impl B for usize {}
|
||||||
|
|
||||||
|
trait Mirror {
|
||||||
|
type Assoc: ?Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Mirror for T {
|
||||||
|
type Assoc = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = Box::new(1usize) as Box<<dyn B as Mirror>::Assoc>;
|
||||||
|
let y = x as Box<<dyn A as Mirror>::Assoc>;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue