Skip to content

Commit 7e66c0b

Browse files
Normalize the RHS of an unsize goal
1 parent 23405bb commit 7e66c0b

File tree

5 files changed

+168
-68
lines changed

5 files changed

+168
-68
lines changed

compiler/rustc_middle/src/traits/solve/inspect.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> {
7373
pub enum CandidateKind<'tcx> {
7474
/// Probe entered when normalizing the self ty during candidate assembly
7575
NormalizedSelfTyAssembly,
76+
DynUpcastingAssembly,
7677
/// A normal candidate for proving a goal
77-
Candidate { name: String, result: QueryResult<'tcx> },
78+
Candidate {
79+
name: String,
80+
result: QueryResult<'tcx>,
81+
},
7882
}
7983
impl Debug for GoalCandidate<'_> {
8084
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

compiler/rustc_middle/src/traits/solve/inspect/format.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
100100
CandidateKind::NormalizedSelfTyAssembly => {
101101
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
102102
}
103+
CandidateKind::DynUpcastingAssembly => {
104+
writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:")
105+
}
103106
CandidateKind::Candidate { name, result } => {
104107
writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
105108
}

compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,13 @@ fn rematch_unsize<'tcx>(
330330
mut nested: Vec<PredicateObligation<'tcx>>,
331331
) -> SelectionResult<'tcx, Selection<'tcx>> {
332332
let tcx = infcx.tcx;
333-
let a_ty = goal.predicate.self_ty();
334-
let b_ty = goal.predicate.trait_ref.args.type_at(1);
335-
333+
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
334+
let b_ty = structurally_normalize(
335+
goal.predicate.trait_ref.args.type_at(1),
336+
infcx,
337+
goal.param_env,
338+
&mut nested,
339+
);
336340
match (a_ty.kind(), b_ty.kind()) {
337341
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
338342
// Check that the type implements all of the predicates of the def-id.

compiler/rustc_trait_selection/src/solve/trait_goals.rs

Lines changed: 132 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
22
33
use super::assembly::{self, structural_traits};
4+
use super::search_graph::OverflowHandler;
45
use super::{EvalCtxt, SolverMode};
56
use rustc_hir::def_id::DefId;
67
use rustc_hir::{LangItem, Movability};
78
use rustc_infer::traits::query::NoSolution;
89
use rustc_infer::traits::util::supertraits;
9-
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
10+
use rustc_middle::traits::solve::inspect::CandidateKind;
11+
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
1012
use rustc_middle::traits::Reveal;
1113
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
1214
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
@@ -376,11 +378,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
376378
let tcx = ecx.tcx();
377379
let a_ty = goal.predicate.self_ty();
378380
let b_ty = goal.predicate.trait_ref.args.type_at(1);
379-
if b_ty.is_ty_var() {
380-
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
381-
}
381+
382382
ecx.probe_candidate("builtin unsize").enter(|ecx| {
383+
let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else {
384+
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
385+
MaybeCause::Overflow,
386+
));
387+
};
388+
383389
match (a_ty.kind(), b_ty.kind()) {
390+
(_, ty::Infer(ty::TyVar(_))) => {
391+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
392+
}
384393
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
385394
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
386395
// Dyn upcasting is handled separately, since due to upcasting,
@@ -489,74 +498,90 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
489498

490499
let tcx = ecx.tcx();
491500

492-
let a_ty = goal.predicate.self_ty();
493-
let b_ty = goal.predicate.trait_ref.args.type_at(1);
494-
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
495-
return vec![];
496-
};
497-
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
498-
return vec![];
499-
};
501+
// Need to wrap in a probe since `normalize_non_self_ty` has side-effects.
502+
ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| {
503+
let a_ty = goal.predicate.self_ty();
504+
let b_ty = goal.predicate.trait_ref.args.type_at(1);
505+
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
506+
return vec![];
507+
};
500508

501-
// All of a's auto traits need to be in b's auto traits.
502-
let auto_traits_compatible =
503-
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
504-
if !auto_traits_compatible {
505-
return vec![];
506-
}
509+
// We don't care about `ty::Infer` here or errors here, since we'll
510+
// register an ambiguous/error response in the other unsize candidate
511+
// assembly function.
512+
let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else {
513+
return vec![];
514+
};
515+
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
516+
return vec![];
517+
};
507518

508-
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
509-
ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
510-
// Require that all of the trait predicates from A match B, except for
511-
// the auto traits. We do this by constructing a new A type with B's
512-
// auto traits, and equating these types.
513-
let new_a_data = principal
514-
.into_iter()
515-
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
516-
.chain(a_data.iter().filter(|a| {
517-
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
518-
}))
519-
.chain(
520-
b_data
521-
.auto_traits()
522-
.map(ty::ExistentialPredicate::AutoTrait)
523-
.map(ty::Binder::dummy),
524-
);
525-
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
526-
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
519+
// All of a's auto traits need to be in b's auto traits.
520+
let auto_traits_compatible =
521+
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
522+
if !auto_traits_compatible {
523+
return vec![];
524+
}
527525

528-
// We also require that A's lifetime outlives B's lifetime.
529-
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
530-
ecx.add_goal(
531-
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
532-
);
533-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
534-
})
535-
};
526+
let mut unsize_dyn_to_principal = |principal: Option<
527+
ty::PolyExistentialTraitRef<'tcx>,
528+
>| {
529+
ecx.probe_candidate("upcast dyn to principle").enter(
530+
|ecx| -> Result<_, NoSolution> {
531+
// Require that all of the trait predicates from A match B, except for
532+
// the auto traits. We do this by constructing a new A type with B's
533+
// auto traits, and equating these types.
534+
let new_a_data = principal
535+
.into_iter()
536+
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
537+
.chain(a_data.iter().filter(|a| {
538+
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
539+
}))
540+
.chain(
541+
b_data
542+
.auto_traits()
543+
.map(ty::ExistentialPredicate::AutoTrait)
544+
.map(ty::Binder::dummy),
545+
);
546+
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
547+
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
548+
549+
// We also require that A's lifetime outlives B's lifetime.
550+
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
551+
ecx.add_goal(goal.with(
552+
tcx,
553+
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
554+
));
555+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
556+
},
557+
)
558+
};
536559

537-
let mut responses = vec![];
538-
// If the principal def ids match (or are both none), then we're not doing
539-
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
540-
if a_data.principal_def_id() == b_data.principal_def_id() {
541-
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
542-
responses.push(response);
543-
}
544-
} else if let Some(a_principal) = a_data.principal()
545-
&& let Some(b_principal) = b_data.principal()
546-
{
547-
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
548-
if super_trait_ref.def_id() != b_principal.def_id() {
549-
continue;
550-
}
551-
let erased_trait_ref = super_trait_ref
552-
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
553-
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
560+
let mut responses = vec![];
561+
// If the principal def ids match (or are both none), then we're not doing
562+
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
563+
if a_data.principal_def_id() == b_data.principal_def_id() {
564+
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
554565
responses.push(response);
555566
}
567+
} else if let Some(a_principal) = a_data.principal()
568+
&& let Some(b_principal) = b_data.principal()
569+
{
570+
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
571+
if super_trait_ref.def_id() != b_principal.def_id() {
572+
continue;
573+
}
574+
let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| {
575+
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
576+
});
577+
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
578+
responses.push(response);
579+
}
580+
}
556581
}
557-
}
558582

559-
responses
583+
responses
584+
})
560585
}
561586

562587
fn consider_builtin_discriminant_kind_candidate(
@@ -750,4 +775,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
750775
let candidates = self.assemble_and_evaluate_candidates(goal);
751776
self.merge_candidates(candidates)
752777
}
778+
779+
/// Normalize a non-self type when it is structually matched on when solving
780+
/// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty`
781+
/// for the self type, but for other goals, additional normalization of other
782+
/// arguments may be needed to completely implement the semantics of the trait.
783+
///
784+
/// This is required when structurally matching on any trait argument that is
785+
/// not the self type.
786+
fn normalize_non_self_ty(
787+
&mut self,
788+
mut ty: Ty<'tcx>,
789+
param_env: ty::ParamEnv<'tcx>,
790+
) -> Result<Option<Ty<'tcx>>, NoSolution> {
791+
if !matches!(ty.kind(), ty::Alias(..)) {
792+
return Ok(Some(ty));
793+
}
794+
795+
self.repeat_while_none(
796+
|_| Ok(None),
797+
|ecx| {
798+
let ty::Alias(_, projection_ty) = *ty.kind() else {
799+
return Some(Ok(Some(ty)));
800+
};
801+
802+
let normalized_ty = ecx.next_ty_infer();
803+
let normalizes_to_goal = Goal::new(
804+
ecx.tcx(),
805+
param_env,
806+
ty::Binder::dummy(ty::ProjectionPredicate {
807+
projection_ty,
808+
term: normalized_ty.into(),
809+
}),
810+
);
811+
ecx.add_goal(normalizes_to_goal);
812+
if let Err(err) = ecx.try_evaluate_added_goals() {
813+
return Some(Err(err));
814+
}
815+
816+
ty = ecx.resolve_vars_if_possible(normalized_ty);
817+
None
818+
},
819+
)
820+
}
753821
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
trait A {}
5+
trait B: A {}
6+
7+
impl A for usize {}
8+
impl B for usize {}
9+
10+
trait Mirror {
11+
type Assoc: ?Sized;
12+
}
13+
14+
impl<T: ?Sized> Mirror for T {
15+
type Assoc = T;
16+
}
17+
18+
fn main() {
19+
let x = Box::new(1usize) as Box<<dyn B as Mirror>::Assoc>;
20+
let y = x as Box<<dyn A as Mirror>::Assoc>;
21+
}

0 commit comments

Comments
 (0)