Skip to content

Commit 50127a6

Browse files
authored
Rollup merge of rust-lang#91096 - compiler-errors:elaborate_opaque_trait, r=estebank
Print associated types on opaque `impl Trait` types This PR generalizes rust-lang#91021, printing associated types for all opaque `impl Trait` types instead of just special-casing for future. before: ``` error[E0271]: type mismatch resolving `<impl Iterator as Iterator>::Item == u32` ``` after: ``` error[E0271]: type mismatch resolving `<impl Iterator<Item = usize> as Iterator>::Item == u32` ``` --- Questions: 1. I'm kinda lost in binders hell with this one. Is all of the `rebind`ing necessary? 2. Is there a map collection type that will give me a stable iteration order? Doesn't seem like TraitRef is Ord, so I can't just sort later.. 3. I removed the logic that suppresses printing generator projection types. It creates outputs like this [gist](https://gist.github.com/compiler-errors/d6f12fb30079feb1ad1d5f1ab39a3a8d). Should I put that back? 4. I also added spaces between traits, `impl A+B` -> `impl A + B`. I quite like this change, but is there a good reason to keep it like that? r? ```@estebank```
2 parents 57e784e + 9cc1179 commit 50127a6

36 files changed

+355
-147
lines changed

compiler/rustc_hir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ language_item_table! {
268268
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
269269
GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None;
270270
Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
271+
GeneratorReturn, sym::generator_return, generator_return, Target::AssocTy, GenericRequirement::None;
271272
Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
272273
Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
273274

compiler/rustc_middle/src/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod query;
77
pub mod select;
88
pub mod specialization_graph;
99
mod structural_impls;
10+
pub mod util;
1011

1112
use crate::infer::canonical::Canonical;
1213
use crate::thir::abstract_const::NotConstEvaluatable;
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use rustc_data_structures::stable_set::FxHashSet;
2+
3+
use crate::ty::{PolyTraitRef, TyCtxt};
4+
5+
/// Given a PolyTraitRef, get the PolyTraitRefs of the trait's (transitive) supertraits.
6+
///
7+
/// A simplfied version of the same function at `rustc_infer::traits::util::supertraits`.
8+
pub fn supertraits<'tcx>(
9+
tcx: TyCtxt<'tcx>,
10+
trait_ref: PolyTraitRef<'tcx>,
11+
) -> impl Iterator<Item = PolyTraitRef<'tcx>> {
12+
Elaborator { tcx, visited: FxHashSet::from_iter([trait_ref]), stack: vec![trait_ref] }
13+
}
14+
15+
struct Elaborator<'tcx> {
16+
tcx: TyCtxt<'tcx>,
17+
visited: FxHashSet<PolyTraitRef<'tcx>>,
18+
stack: Vec<PolyTraitRef<'tcx>>,
19+
}
20+
21+
impl<'tcx> Elaborator<'tcx> {
22+
fn elaborate(&mut self, trait_ref: PolyTraitRef<'tcx>) {
23+
let supertrait_refs = self
24+
.tcx
25+
.super_predicates_of(trait_ref.def_id())
26+
.predicates
27+
.into_iter()
28+
.flat_map(|(pred, _)| {
29+
pred.subst_supertrait(self.tcx, &trait_ref).to_opt_poly_trait_ref()
30+
})
31+
.map(|t| t.value)
32+
.filter(|supertrait_ref| self.visited.insert(*supertrait_ref));
33+
34+
self.stack.extend(supertrait_refs);
35+
}
36+
}
37+
38+
impl<'tcx> Iterator for Elaborator<'tcx> {
39+
type Item = PolyTraitRef<'tcx>;
40+
41+
fn next(&mut self) -> Option<PolyTraitRef<'tcx>> {
42+
if let Some(trait_ref) = self.stack.pop() {
43+
self.elaborate(trait_ref);
44+
Some(trait_ref)
45+
} else {
46+
None
47+
}
48+
}
49+
}

compiler/rustc_middle/src/ty/print/pretty.rs

+229-74
Original file line numberDiff line numberDiff line change
@@ -643,81 +643,8 @@ pub trait PrettyPrinter<'tcx>:
643643
}
644644
return Ok(self);
645645
}
646-
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
647-
// by looking up the projections associated with the def_id.
648-
let bounds = self.tcx().explicit_item_bounds(def_id);
649-
650-
let mut first = true;
651-
let mut is_sized = false;
652-
let mut is_future = false;
653-
let mut future_output_ty = None;
654-
655-
p!("impl");
656-
for (predicate, _) in bounds {
657-
let predicate = predicate.subst(self.tcx(), substs);
658-
let bound_predicate = predicate.kind();
659-
660-
match bound_predicate.skip_binder() {
661-
ty::PredicateKind::Projection(projection_predicate) => {
662-
let Some(future_trait) = self.tcx().lang_items().future_trait() else { continue };
663-
let future_output_def_id =
664-
self.tcx().associated_item_def_ids(future_trait)[0];
665-
666-
if projection_predicate.projection_ty.item_def_id
667-
== future_output_def_id
668-
{
669-
// We don't account for multiple `Future::Output = Ty` contraints.
670-
is_future = true;
671-
future_output_ty = Some(projection_predicate.ty);
672-
}
673-
}
674-
ty::PredicateKind::Trait(pred) => {
675-
let trait_ref = bound_predicate.rebind(pred.trait_ref);
676-
// Don't print +Sized, but rather +?Sized if absent.
677-
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait()
678-
{
679-
is_sized = true;
680-
continue;
681-
}
682-
683-
if Some(trait_ref.def_id())
684-
== self.tcx().lang_items().future_trait()
685-
{
686-
is_future = true;
687-
continue;
688-
}
689-
690-
p!(
691-
write("{}", if first { " " } else { " + " }),
692-
print(trait_ref.print_only_trait_path())
693-
);
694-
695-
first = false;
696-
}
697-
_ => {}
698-
}
699-
}
700-
701-
if is_future {
702-
p!(write("{}Future", if first { " " } else { " + " }));
703-
first = false;
704646

705-
if let Some(future_output_ty) = future_output_ty {
706-
// Don't print projection types, which we (unfortunately) see often
707-
// in the error outputs involving async blocks.
708-
if !matches!(future_output_ty.kind(), ty::Projection(_)) {
709-
p!("<Output = ", print(future_output_ty), ">");
710-
}
711-
}
712-
}
713-
714-
if !is_sized {
715-
p!(write("{}?Sized", if first { " " } else { " + " }));
716-
} else if first {
717-
p!(" Sized");
718-
}
719-
720-
Ok(self)
647+
self.pretty_print_opaque_impl_type(def_id, substs)
721648
});
722649
}
723650
ty::Str => p!("str"),
@@ -826,6 +753,225 @@ pub trait PrettyPrinter<'tcx>:
826753
Ok(self)
827754
}
828755

756+
fn pretty_print_opaque_impl_type(
757+
mut self,
758+
def_id: DefId,
759+
substs: &'tcx ty::List<ty::GenericArg<'tcx>>,
760+
) -> Result<Self::Type, Self::Error> {
761+
define_scoped_cx!(self);
762+
763+
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
764+
// by looking up the projections associated with the def_id.
765+
let bounds = self.tcx().explicit_item_bounds(def_id);
766+
767+
let mut traits = BTreeMap::new();
768+
let mut fn_traits = BTreeMap::new();
769+
let mut is_sized = false;
770+
771+
for (predicate, _) in bounds {
772+
let predicate = predicate.subst(self.tcx(), substs);
773+
let bound_predicate = predicate.kind();
774+
775+
match bound_predicate.skip_binder() {
776+
ty::PredicateKind::Trait(pred) => {
777+
let trait_ref = bound_predicate.rebind(pred.trait_ref);
778+
779+
// Don't print + Sized, but rather + ?Sized if absent.
780+
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
781+
is_sized = true;
782+
continue;
783+
}
784+
785+
self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits);
786+
}
787+
ty::PredicateKind::Projection(pred) => {
788+
let proj_ref = bound_predicate.rebind(pred);
789+
let trait_ref = proj_ref.required_poly_trait_ref(self.tcx());
790+
791+
// Projection type entry -- the def-id for naming, and the ty.
792+
let proj_ty = (proj_ref.projection_def_id(), proj_ref.ty());
793+
794+
self.insert_trait_and_projection(
795+
trait_ref,
796+
Some(proj_ty),
797+
&mut traits,
798+
&mut fn_traits,
799+
);
800+
}
801+
_ => {}
802+
}
803+
}
804+
805+
let mut first = true;
806+
// Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait
807+
let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized;
808+
809+
p!("impl");
810+
811+
for (fn_once_trait_ref, entry) in fn_traits {
812+
// Get the (single) generic ty (the args) of this FnOnce trait ref.
813+
let generics = self.generic_args_to_print(
814+
self.tcx().generics_of(fn_once_trait_ref.def_id()),
815+
fn_once_trait_ref.skip_binder().substs,
816+
);
817+
818+
match (entry.return_ty, generics[0].expect_ty()) {
819+
// We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded
820+
// a return type.
821+
(Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => {
822+
let name = if entry.fn_trait_ref.is_some() {
823+
"Fn"
824+
} else if entry.fn_mut_trait_ref.is_some() {
825+
"FnMut"
826+
} else {
827+
"FnOnce"
828+
};
829+
830+
p!(
831+
write("{}", if first { " " } else { " + " }),
832+
write("{}{}(", if paren_needed { "(" } else { "" }, name)
833+
);
834+
835+
for (idx, ty) in arg_tys.tuple_fields().enumerate() {
836+
if idx > 0 {
837+
p!(", ");
838+
}
839+
p!(print(ty));
840+
}
841+
842+
p!(")");
843+
if !return_ty.skip_binder().is_unit() {
844+
p!("-> ", print(return_ty));
845+
}
846+
p!(write("{}", if paren_needed { ")" } else { "" }));
847+
848+
first = false;
849+
}
850+
// If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the
851+
// trait_refs we collected in the OpaqueFnEntry as normal trait refs.
852+
_ => {
853+
if entry.has_fn_once {
854+
traits.entry(fn_once_trait_ref).or_default().extend(
855+
// Group the return ty with its def id, if we had one.
856+
entry
857+
.return_ty
858+
.map(|ty| (self.tcx().lang_items().fn_once_output().unwrap(), ty)),
859+
);
860+
}
861+
if let Some(trait_ref) = entry.fn_mut_trait_ref {
862+
traits.entry(trait_ref).or_default();
863+
}
864+
if let Some(trait_ref) = entry.fn_trait_ref {
865+
traits.entry(trait_ref).or_default();
866+
}
867+
}
868+
}
869+
}
870+
871+
// Print the rest of the trait types (that aren't Fn* family of traits)
872+
for (trait_ref, assoc_items) in traits {
873+
p!(
874+
write("{}", if first { " " } else { " + " }),
875+
print(trait_ref.skip_binder().print_only_trait_name())
876+
);
877+
878+
let generics = self.generic_args_to_print(
879+
self.tcx().generics_of(trait_ref.def_id()),
880+
trait_ref.skip_binder().substs,
881+
);
882+
883+
if !generics.is_empty() || !assoc_items.is_empty() {
884+
p!("<");
885+
let mut first = true;
886+
887+
for ty in generics {
888+
if !first {
889+
p!(", ");
890+
}
891+
p!(print(trait_ref.rebind(*ty)));
892+
first = false;
893+
}
894+
895+
for (assoc_item_def_id, ty) in assoc_items {
896+
if !first {
897+
p!(", ");
898+
}
899+
p!(write("{} = ", self.tcx().associated_item(assoc_item_def_id).ident));
900+
901+
// Skip printing `<[generator@] as Generator<_>>::Return` from async blocks
902+
match ty.skip_binder().kind() {
903+
ty::Projection(ty::ProjectionTy { item_def_id, .. })
904+
if Some(*item_def_id) == self.tcx().lang_items().generator_return() =>
905+
{
906+
p!("[async output]")
907+
}
908+
_ => {
909+
p!(print(ty))
910+
}
911+
}
912+
913+
first = false;
914+
}
915+
916+
p!(">");
917+
}
918+
919+
first = false;
920+
}
921+
922+
if !is_sized {
923+
p!(write("{}?Sized", if first { " " } else { " + " }));
924+
} else if first {
925+
p!(" Sized");
926+
}
927+
928+
Ok(self)
929+
}
930+
931+
/// Insert the trait ref and optionally a projection type associated with it into either the
932+
/// traits map or fn_traits map, depending on if the trait is in the Fn* family of traits.
933+
fn insert_trait_and_projection(
934+
&mut self,
935+
trait_ref: ty::PolyTraitRef<'tcx>,
936+
proj_ty: Option<(DefId, ty::Binder<'tcx, Ty<'tcx>>)>,
937+
traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, BTreeMap<DefId, ty::Binder<'tcx, Ty<'tcx>>>>,
938+
fn_traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
939+
) {
940+
let trait_def_id = trait_ref.def_id();
941+
942+
// If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce
943+
// super-trait ref and record it there.
944+
if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() {
945+
// If we have a FnOnce, then insert it into
946+
if trait_def_id == fn_once_trait {
947+
let entry = fn_traits.entry(trait_ref).or_default();
948+
// Optionally insert the return_ty as well.
949+
if let Some((_, ty)) = proj_ty {
950+
entry.return_ty = Some(ty);
951+
}
952+
entry.has_fn_once = true;
953+
return;
954+
} else if Some(trait_def_id) == self.tcx().lang_items().fn_mut_trait() {
955+
let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref)
956+
.find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
957+
.unwrap();
958+
959+
fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref);
960+
return;
961+
} else if Some(trait_def_id) == self.tcx().lang_items().fn_trait() {
962+
let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref)
963+
.find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
964+
.unwrap();
965+
966+
fn_traits.entry(super_trait_ref).or_default().fn_trait_ref = Some(trait_ref);
967+
return;
968+
}
969+
}
970+
971+
// Otherwise, just group our traits and projection types.
972+
traits.entry(trait_ref).or_default().extend(proj_ty);
973+
}
974+
829975
fn pretty_print_bound_var(
830976
&mut self,
831977
debruijn: ty::DebruijnIndex,
@@ -2553,3 +2699,12 @@ fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap<DefId, Symbol> {
25532699
pub fn provide(providers: &mut ty::query::Providers) {
25542700
*providers = ty::query::Providers { trimmed_def_paths, ..*providers };
25552701
}
2702+
2703+
#[derive(Default)]
2704+
pub struct OpaqueFnEntry<'tcx> {
2705+
// The trait ref is already stored as a key, so just track if we have it as a real predicate
2706+
has_fn_once: bool,
2707+
fn_mut_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
2708+
fn_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
2709+
return_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>,
2710+
}

compiler/rustc_middle/src/ty/sty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ impl<'tcx> List<ty::Binder<'tcx, ExistentialPredicate<'tcx>>> {
890890
///
891891
/// Trait references also appear in object types like `Foo<U>`, but in
892892
/// that case the `Self` parameter is absent from the substitutions.
893-
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
893+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
894894
#[derive(HashStable, TypeFoldable)]
895895
pub struct TraitRef<'tcx> {
896896
pub def_id: DefId,

0 commit comments

Comments
 (0)