Skip to content

Commit ce182d1

Browse files
Elaborate trait generics and associated types
1 parent c969b1d commit ce182d1

File tree

1 file changed

+210
-74
lines changed

1 file changed

+210
-74
lines changed

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

+210-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-
}
700646

701-
if is_future {
702-
p!(write("{}Future", if first { " " } else { " + " }));
703-
first = false;
704-
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,208 @@ 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!(") -> ", print(return_ty), write("{}", if paren_needed { ")" } else { "" }));
843+
844+
first = false;
845+
}
846+
// If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the
847+
// trait_refs we collected in the OpaqueFnEntry as normal trait refs.
848+
_ => {
849+
traits.entry(fn_once_trait_ref).or_default().extend(
850+
// Group the return ty with its def id, if we had one.
851+
entry
852+
.return_ty
853+
.map(|ty| (self.tcx().lang_items().fn_once_output().unwrap(), ty)),
854+
);
855+
if let Some(trait_ref) = entry.fn_mut_trait_ref {
856+
traits.entry(trait_ref).or_default();
857+
}
858+
if let Some(trait_ref) = entry.fn_trait_ref {
859+
traits.entry(trait_ref).or_default();
860+
}
861+
}
862+
}
863+
}
864+
865+
// Print the rest of the trait types (that aren't Fn* family of traits)
866+
for (trait_ref, assoc_items) in traits {
867+
p!(
868+
write("{}", if first { " " } else { " + " }),
869+
print(trait_ref.skip_binder().print_only_trait_name())
870+
);
871+
872+
let generics = self.generic_args_to_print(
873+
self.tcx().generics_of(trait_ref.def_id()),
874+
trait_ref.skip_binder().substs,
875+
);
876+
877+
if !generics.is_empty() || !assoc_items.is_empty() {
878+
p!("<");
879+
let mut first = true;
880+
881+
for ty in generics {
882+
if !first {
883+
p!(", ");
884+
}
885+
p!(print(trait_ref.rebind(*ty)));
886+
first = false;
887+
}
888+
889+
for (assoc_item_def_id, ty) in assoc_items {
890+
if !first {
891+
p!(", ");
892+
}
893+
p!(
894+
write("{} = ", self.tcx().associated_item(assoc_item_def_id).ident),
895+
print(ty)
896+
);
897+
first = false;
898+
}
899+
900+
p!(">");
901+
}
902+
903+
first = false;
904+
}
905+
906+
if !is_sized {
907+
p!(write("{}?Sized", if first { " " } else { " + " }));
908+
} else if first {
909+
p!(" Sized");
910+
}
911+
912+
Ok(self)
913+
}
914+
915+
/// Insert the trait ref and optionally a projection type associated with it into either the
916+
/// traits map or fn_traits map, depending on if the trait is in the Fn* family of traits.
917+
fn insert_trait_and_projection(
918+
&mut self,
919+
trait_ref: ty::PolyTraitRef<'tcx>,
920+
proj_ty: Option<(DefId, ty::Binder<'tcx, Ty<'tcx>>)>,
921+
traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, BTreeMap<DefId, ty::Binder<'tcx, Ty<'tcx>>>>,
922+
fn_traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
923+
) {
924+
let trait_def_id = trait_ref.def_id();
925+
926+
// If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce
927+
// super-trait ref and record it there.
928+
if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() {
929+
// If we have a FnOnce, then insert it into
930+
if trait_def_id == fn_once_trait {
931+
let entry = fn_traits.entry(trait_ref).or_default();
932+
// Optionally insert the return_ty as well.
933+
if let Some((_, ty)) = proj_ty {
934+
entry.return_ty = Some(ty);
935+
}
936+
return;
937+
} else if Some(trait_def_id) == self.tcx().lang_items().fn_mut_trait() {
938+
let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref)
939+
.find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
940+
.unwrap();
941+
942+
fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref);
943+
return;
944+
} else if Some(trait_def_id) == self.tcx().lang_items().fn_trait() {
945+
let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref)
946+
.find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
947+
.unwrap();
948+
949+
fn_traits.entry(super_trait_ref).or_default().fn_trait_ref = Some(trait_ref);
950+
return;
951+
}
952+
}
953+
954+
// Otherwise, just group our traits and projection types.
955+
traits.entry(trait_ref).or_default().extend(proj_ty);
956+
}
957+
829958
fn pretty_print_bound_var(
830959
&mut self,
831960
debruijn: ty::DebruijnIndex,
@@ -2553,3 +2682,10 @@ fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap<DefId, Symbol> {
25532682
pub fn provide(providers: &mut ty::query::Providers) {
25542683
*providers = ty::query::Providers { trimmed_def_paths, ..*providers };
25552684
}
2685+
2686+
#[derive(Default)]
2687+
pub struct OpaqueFnEntry<'tcx> {
2688+
fn_mut_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
2689+
fn_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
2690+
return_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>,
2691+
}

0 commit comments

Comments
 (0)