Skip to content

Commit ec8e898

Browse files
Consider principal trait ref's auto-trait super-traits in dyn upcasting
1 parent fa9f77f commit ec8e898

File tree

4 files changed

+93
-52
lines changed

4 files changed

+93
-52
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;
@@ -663,13 +666,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
663666
let tcx = self.tcx();
664667
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
665668

666-
// All of a's auto traits need to be in b's auto traits.
667-
let auto_traits_compatible =
668-
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
669-
if !auto_traits_compatible {
670-
return vec![];
671-
}
672-
673669
let mut responses = vec![];
674670
// If the principal def ids match (or are both none), then we're not doing
675671
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
@@ -757,6 +753,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
757753
) -> QueryResult<'tcx> {
758754
let param_env = goal.param_env;
759755

756+
// We may upcast to auto traits that are either explicitly listed in
757+
// the object type's bounds, or implied by the principal trait ref's
758+
// supertraits.
759+
let a_auto_traits: FxIndexSet<DefId> = a_data
760+
.auto_traits()
761+
.chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
762+
supertrait_def_ids(self.tcx(), principal_def_id)
763+
.filter(|def_id| self.tcx().trait_is_auto(*def_id))
764+
}))
765+
.collect();
766+
760767
// More than one projection in a_ty's bounds may match the projection
761768
// in b_ty's bound. Use this to first determine *which* apply without
762769
// having any inference side-effects. We process obligations because
@@ -806,7 +813,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
806813
}
807814
// Check that b_ty's auto traits are present in a_ty's bounds.
808815
ty::ExistentialPredicate::AutoTrait(def_id) => {
809-
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
816+
if !a_auto_traits.contains(&def_id) {
810817
return Err(NoSolution);
811818
}
812819
}

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

+52-43
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::ops::ControlFlow;
1010

1111
use hir::def_id::DefId;
1212
use hir::LangItem;
13-
use rustc_data_structures::fx::FxHashSet;
13+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
1414
use rustc_hir as hir;
1515
use rustc_infer::traits::ObligationCause;
1616
use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
@@ -968,52 +968,61 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
968968
//
969969
// We always perform upcasting coercions when we can because of reason
970970
// #2 (region bounds).
971-
let auto_traits_compatible = b_data
972-
.auto_traits()
973-
// All of a's auto traits need to be in b's auto traits.
974-
.all(|b| a_data.auto_traits().any(|a| a == b));
975-
if auto_traits_compatible {
976-
let principal_def_id_a = a_data.principal_def_id();
977-
let principal_def_id_b = b_data.principal_def_id();
978-
if principal_def_id_a == principal_def_id_b {
979-
// no cyclic
971+
let principal_def_id_a = a_data.principal_def_id();
972+
let principal_def_id_b = b_data.principal_def_id();
973+
if principal_def_id_a == principal_def_id_b {
974+
// We may upcast to auto traits that are either explicitly listed in
975+
// the object type's bounds, or implied by the principal trait ref's
976+
// supertraits.
977+
let a_auto_traits: FxIndexSet<DefId> = a_data
978+
.auto_traits()
979+
.chain(principal_def_id_a.into_iter().flat_map(|principal_def_id| {
980+
util::supertrait_def_ids(self.tcx(), principal_def_id)
981+
.filter(|def_id| self.tcx().trait_is_auto(*def_id))
982+
}))
983+
.collect();
984+
let auto_traits_compatible = b_data
985+
.auto_traits()
986+
// All of a's auto traits need to be in b's auto traits.
987+
.all(|b| a_auto_traits.contains(&b));
988+
if auto_traits_compatible {
980989
candidates.vec.push(BuiltinUnsizeCandidate);
981-
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
982-
// not casual unsizing, now check whether this is trait upcasting coercion.
983-
let principal_a = a_data.principal().unwrap();
984-
let target_trait_did = principal_def_id_b.unwrap();
985-
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
986-
if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
987-
source,
988-
obligation.param_env,
989-
&obligation.cause,
990-
) {
991-
if deref_trait_ref.def_id() == target_trait_did {
992-
return;
993-
}
990+
}
991+
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
992+
// not casual unsizing, now check whether this is trait upcasting coercion.
993+
let principal_a = a_data.principal().unwrap();
994+
let target_trait_did = principal_def_id_b.unwrap();
995+
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
996+
if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
997+
source,
998+
obligation.param_env,
999+
&obligation.cause,
1000+
) {
1001+
if deref_trait_ref.def_id() == target_trait_did {
1002+
return;
9941003
}
1004+
}
9951005

996-
for (idx, upcast_trait_ref) in
997-
util::supertraits(self.tcx(), source_trait_ref).enumerate()
998-
{
999-
self.infcx.probe(|_| {
1000-
if upcast_trait_ref.def_id() == target_trait_did
1001-
&& let Ok(nested) = self.match_upcast_principal(
1002-
obligation,
1003-
upcast_trait_ref,
1004-
a_data,
1005-
b_data,
1006-
a_region,
1007-
b_region,
1008-
)
1009-
{
1010-
if nested.is_none() {
1011-
candidates.ambiguous = true;
1012-
}
1013-
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
1006+
for (idx, upcast_trait_ref) in
1007+
util::supertraits(self.tcx(), source_trait_ref).enumerate()
1008+
{
1009+
self.infcx.probe(|_| {
1010+
if upcast_trait_ref.def_id() == target_trait_did
1011+
&& let Ok(nested) = self.match_upcast_principal(
1012+
obligation,
1013+
upcast_trait_ref,
1014+
a_data,
1015+
b_data,
1016+
a_region,
1017+
b_region,
1018+
)
1019+
{
1020+
if nested.is_none() {
1021+
candidates.ambiguous = true;
10141022
}
1015-
})
1016-
}
1023+
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
1024+
}
1025+
})
10171026
}
10181027
}
10191028
}

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

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

2529+
// We may upcast to auto traits that are either explicitly listed in
2530+
// the object type's bounds, or implied by the principal trait ref's
2531+
// supertraits.
2532+
let a_auto_traits: FxIndexSet<DefId> = a_data
2533+
.auto_traits()
2534+
.chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
2535+
util::supertrait_def_ids(tcx, principal_def_id)
2536+
.filter(|def_id| tcx.trait_is_auto(*def_id))
2537+
}))
2538+
.collect();
2539+
25292540
let upcast_principal = normalize_with_depth_to(
25302541
self,
25312542
obligation.param_env,
@@ -2588,7 +2599,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
25882599
}
25892600
// Check that b_ty's auto traits are present in a_ty's bounds.
25902601
ty::ExistentialPredicate::AutoTrait(def_id) => {
2591-
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
2602+
if !a_auto_traits.contains(&def_id) {
25922603
return Err(SelectionError::Unimplemented);
25932604
}
25942605
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// check-pass
2+
// revisions: current next
3+
//[next] compile-flags: -Znext-solver
4+
5+
#![feature(trait_upcasting)]
6+
7+
trait Target {}
8+
trait Source: Send + Target {}
9+
10+
fn upcast(x: &dyn Source) -> &(dyn Target + Send) { x }
11+
12+
fn same(x: &dyn Source) -> &(dyn Source + Send) { x }
13+
14+
fn main() {}

0 commit comments

Comments
 (0)