Skip to content

Commit 6cfde57

Browse files
Consider principal trait ref's auto-trait super-traits in dyn upcasting
1 parent eee93d8 commit 6cfde57

File tree

3 files changed

+72
-44
lines changed

3 files changed

+72
-44
lines changed

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
22
3+
use crate::traits::supertrait_def_ids;
4+
35
use super::assembly::{self, structural_traits, Candidate};
46
use super::{EvalCtxt, GoalSource, SolverMode};
7+
use rustc_data_structures::fx::FxIndexSet;
58
use rustc_hir::def_id::DefId;
69
use rustc_hir::{LangItem, Movability};
710
use rustc_infer::traits::query::NoSolution;
@@ -644,13 +647,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
644647
let tcx = self.tcx();
645648
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
646649

647-
// All of a's auto traits need to be in b's auto traits.
648-
let auto_traits_compatible =
649-
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
650-
if !auto_traits_compatible {
651-
return vec![];
652-
}
653-
654650
let mut responses = vec![];
655651
// If the principal def ids match (or are both none), then we're not doing
656652
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
@@ -702,6 +698,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
702698
) -> QueryResult<'tcx> {
703699
let param_env = goal.param_env;
704700

701+
// We may upcast to auto traits that are either explicitly listed in
702+
// the object type's bounds, or implied by the principal trait ref's
703+
// supertraits.
704+
let a_auto_traits: FxIndexSet<DefId> = a_data
705+
.auto_traits()
706+
.chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
707+
supertrait_def_ids(self.tcx(), principal_def_id)
708+
.filter(|def_id| self.tcx().trait_is_auto(*def_id))
709+
}))
710+
.collect();
711+
705712
// More than one projection in a_ty's bounds may match the projection
706713
// in b_ty's bound. Use this to first determine *which* apply without
707714
// having any inference side-effects. We process obligations because
@@ -751,7 +758,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
751758
}
752759
// Check that b_ty's auto traits are present in a_ty's bounds.
753760
ty::ExistentialPredicate::AutoTrait(def_id) => {
754-
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
761+
if !a_auto_traits.contains(&def_id) {
755762
return Err(NoSolution);
756763
}
757764
}

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+45-35
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
use hir::def_id::DefId;
1010
use hir::LangItem;
11+
use rustc_data_structures::fx::FxIndexSet;
1112
use rustc_hir as hir;
1213
use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
1314
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
@@ -765,43 +766,52 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
765766
//
766767
// We always perform upcasting coercions when we can because of reason
767768
// #2 (region bounds).
768-
let auto_traits_compatible = b_data
769-
.auto_traits()
770-
// All of a's auto traits need to be in b's auto traits.
771-
.all(|b| a_data.auto_traits().any(|a| a == b));
772-
if auto_traits_compatible {
773-
let principal_def_id_a = a_data.principal_def_id();
774-
let principal_def_id_b = b_data.principal_def_id();
775-
if principal_def_id_a == principal_def_id_b {
776-
// no cyclic
769+
let principal_def_id_a = a_data.principal_def_id();
770+
let principal_def_id_b = b_data.principal_def_id();
771+
if principal_def_id_a == principal_def_id_b {
772+
// We may upcast to auto traits that are either explicitly listed in
773+
// the object type's bounds, or implied by the principal trait ref's
774+
// supertraits.
775+
let a_auto_traits: FxIndexSet<DefId> = a_data
776+
.auto_traits()
777+
.chain(principal_def_id_a.into_iter().flat_map(|principal_def_id| {
778+
util::supertrait_def_ids(self.tcx(), principal_def_id)
779+
.filter(|def_id| self.tcx().trait_is_auto(*def_id))
780+
}))
781+
.collect();
782+
let auto_traits_compatible = b_data
783+
.auto_traits()
784+
// All of a's auto traits need to be in b's auto traits.
785+
.all(|b| a_auto_traits.contains(&b));
786+
if auto_traits_compatible {
777787
candidates.vec.push(BuiltinUnsizeCandidate);
778-
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
779-
// not casual unsizing, now check whether this is trait upcasting coercion.
780-
let principal_a = a_data.principal().unwrap();
781-
let target_trait_did = principal_def_id_b.unwrap();
782-
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
783-
784-
for (idx, upcast_trait_ref) in
785-
util::supertraits(self.tcx(), source_trait_ref).enumerate()
786-
{
787-
self.infcx.probe(|_| {
788-
if upcast_trait_ref.def_id() == target_trait_did
789-
&& let Ok(nested) = self.match_upcast_principal(
790-
obligation,
791-
upcast_trait_ref,
792-
a_data,
793-
b_data,
794-
a_region,
795-
b_region,
796-
)
797-
{
798-
if nested.is_none() {
799-
candidates.ambiguous = true;
800-
}
801-
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
788+
}
789+
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
790+
// not casual unsizing, now check whether this is trait upcasting coercion.
791+
let principal_a = a_data.principal().unwrap();
792+
let target_trait_did = principal_def_id_b.unwrap();
793+
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
794+
795+
for (idx, upcast_trait_ref) in
796+
util::supertraits(self.tcx(), source_trait_ref).enumerate()
797+
{
798+
self.infcx.probe(|_| {
799+
if upcast_trait_ref.def_id() == target_trait_did
800+
&& let Ok(nested) = self.match_upcast_principal(
801+
obligation,
802+
upcast_trait_ref,
803+
a_data,
804+
b_data,
805+
a_region,
806+
b_region,
807+
)
808+
{
809+
if nested.is_none() {
810+
candidates.ambiguous = true;
802811
}
803-
})
804-
}
812+
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
813+
}
814+
})
805815
}
806816
}
807817
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -2515,6 +2515,17 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
25152515
let tcx = self.tcx();
25162516
let mut nested = vec![];
25172517

2518+
// We may upcast to auto traits that are either explicitly listed in
2519+
// the object type's bounds, or implied by the principal trait ref's
2520+
// supertraits.
2521+
let a_auto_traits: FxIndexSet<DefId> = a_data
2522+
.auto_traits()
2523+
.chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
2524+
util::supertrait_def_ids(tcx, principal_def_id)
2525+
.filter(|def_id| tcx.trait_is_auto(*def_id))
2526+
}))
2527+
.collect();
2528+
25182529
let upcast_principal = normalize_with_depth_to(
25192530
self,
25202531
obligation.param_env,
@@ -2577,7 +2588,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
25772588
}
25782589
// Check that b_ty's auto traits are present in a_ty's bounds.
25792590
ty::ExistentialPredicate::AutoTrait(def_id) => {
2580-
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
2591+
if !a_auto_traits.contains(&def_id) {
25812592
return Err(SelectionError::Unimplemented);
25822593
}
25832594
}

0 commit comments

Comments
 (0)