Elaborate trait generics and associated types

This commit is contained in:
Michael Goulet 2021-11-19 20:51:19 -08:00
parent c969b1dea0
commit ce182d17f8

View File

@ -643,81 +643,8 @@ pub trait PrettyPrinter<'tcx>:
}
return Ok(self);
}
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
// by looking up the projections associated with the def_id.
let bounds = self.tcx().explicit_item_bounds(def_id);
let mut first = true;
let mut is_sized = false;
let mut is_future = false;
let mut future_output_ty = None;
p!("impl");
for (predicate, _) in bounds {
let predicate = predicate.subst(self.tcx(), substs);
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Projection(projection_predicate) => {
let Some(future_trait) = self.tcx().lang_items().future_trait() else { continue };
let future_output_def_id =
self.tcx().associated_item_def_ids(future_trait)[0];
if projection_predicate.projection_ty.item_def_id
== future_output_def_id
{
// We don't account for multiple `Future::Output = Ty` contraints.
is_future = true;
future_output_ty = Some(projection_predicate.ty);
}
}
ty::PredicateKind::Trait(pred) => {
let trait_ref = bound_predicate.rebind(pred.trait_ref);
// Don't print +Sized, but rather +?Sized if absent.
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait()
{
is_sized = true;
continue;
}
if Some(trait_ref.def_id())
== self.tcx().lang_items().future_trait()
{
is_future = true;
continue;
}
p!(
write("{}", if first { " " } else { " + " }),
print(trait_ref.print_only_trait_path())
);
first = false;
}
_ => {}
}
}
if is_future {
p!(write("{}Future", if first { " " } else { " + " }));
first = false;
if let Some(future_output_ty) = future_output_ty {
// Don't print projection types, which we (unfortunately) see often
// in the error outputs involving async blocks.
if !matches!(future_output_ty.kind(), ty::Projection(_)) {
p!("<Output = ", print(future_output_ty), ">");
}
}
}
if !is_sized {
p!(write("{}?Sized", if first { " " } else { " + " }));
} else if first {
p!(" Sized");
}
Ok(self)
self.pretty_print_opaque_impl_type(def_id, substs)
});
}
ty::Str => p!("str"),
@ -826,6 +753,208 @@ pub trait PrettyPrinter<'tcx>:
Ok(self)
}
fn pretty_print_opaque_impl_type(
mut self,
def_id: DefId,
substs: &'tcx ty::List<ty::GenericArg<'tcx>>,
) -> Result<Self::Type, Self::Error> {
define_scoped_cx!(self);
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
// by looking up the projections associated with the def_id.
let bounds = self.tcx().explicit_item_bounds(def_id);
let mut traits = BTreeMap::new();
let mut fn_traits = BTreeMap::new();
let mut is_sized = false;
for (predicate, _) in bounds {
let predicate = predicate.subst(self.tcx(), substs);
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Trait(pred) => {
let trait_ref = bound_predicate.rebind(pred.trait_ref);
// Don't print + Sized, but rather + ?Sized if absent.
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
is_sized = true;
continue;
}
self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits);
}
ty::PredicateKind::Projection(pred) => {
let proj_ref = bound_predicate.rebind(pred);
let trait_ref = proj_ref.required_poly_trait_ref(self.tcx());
// Projection type entry -- the def-id for naming, and the ty.
let proj_ty = (proj_ref.projection_def_id(), proj_ref.ty());
self.insert_trait_and_projection(
trait_ref,
Some(proj_ty),
&mut traits,
&mut fn_traits,
);
}
_ => {}
}
}
let mut first = true;
// Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait
let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized;
p!("impl");
for (fn_once_trait_ref, entry) in fn_traits {
// Get the (single) generic ty (the args) of this FnOnce trait ref.
let generics = self.generic_args_to_print(
self.tcx().generics_of(fn_once_trait_ref.def_id()),
fn_once_trait_ref.skip_binder().substs,
);
match (entry.return_ty, generics[0].expect_ty()) {
// We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded
// a return type.
(Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => {
let name = if entry.fn_trait_ref.is_some() {
"Fn"
} else if entry.fn_mut_trait_ref.is_some() {
"FnMut"
} else {
"FnOnce"
};
p!(
write("{}", if first { " " } else { " + " }),
write("{}{}(", if paren_needed { "(" } else { "" }, name)
);
for (idx, ty) in arg_tys.tuple_fields().enumerate() {
if idx > 0 {
p!(", ");
}
p!(print(ty));
}
p!(") -> ", print(return_ty), write("{}", if paren_needed { ")" } else { "" }));
first = false;
}
// If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the
// trait_refs we collected in the OpaqueFnEntry as normal trait refs.
_ => {
traits.entry(fn_once_trait_ref).or_default().extend(
// Group the return ty with its def id, if we had one.
entry
.return_ty
.map(|ty| (self.tcx().lang_items().fn_once_output().unwrap(), ty)),
);
if let Some(trait_ref) = entry.fn_mut_trait_ref {
traits.entry(trait_ref).or_default();
}
if let Some(trait_ref) = entry.fn_trait_ref {
traits.entry(trait_ref).or_default();
}
}
}
}
// Print the rest of the trait types (that aren't Fn* family of traits)
for (trait_ref, assoc_items) in traits {
p!(
write("{}", if first { " " } else { " + " }),
print(trait_ref.skip_binder().print_only_trait_name())
);
let generics = self.generic_args_to_print(
self.tcx().generics_of(trait_ref.def_id()),
trait_ref.skip_binder().substs,
);
if !generics.is_empty() || !assoc_items.is_empty() {
p!("<");
let mut first = true;
for ty in generics {
if !first {
p!(", ");
}
p!(print(trait_ref.rebind(*ty)));
first = false;
}
for (assoc_item_def_id, ty) in assoc_items {
if !first {
p!(", ");
}
p!(
write("{} = ", self.tcx().associated_item(assoc_item_def_id).ident),
print(ty)
);
first = false;
}
p!(">");
}
first = false;
}
if !is_sized {
p!(write("{}?Sized", if first { " " } else { " + " }));
} else if first {
p!(" Sized");
}
Ok(self)
}
/// Insert the trait ref and optionally a projection type associated with it into either the
/// traits map or fn_traits map, depending on if the trait is in the Fn* family of traits.
fn insert_trait_and_projection(
&mut self,
trait_ref: ty::PolyTraitRef<'tcx>,
proj_ty: Option<(DefId, ty::Binder<'tcx, Ty<'tcx>>)>,
traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, BTreeMap<DefId, ty::Binder<'tcx, Ty<'tcx>>>>,
fn_traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
) {
let trait_def_id = trait_ref.def_id();
// If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce
// super-trait ref and record it there.
if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() {
// If we have a FnOnce, then insert it into
if trait_def_id == fn_once_trait {
let entry = fn_traits.entry(trait_ref).or_default();
// Optionally insert the return_ty as well.
if let Some((_, ty)) = proj_ty {
entry.return_ty = Some(ty);
}
return;
} else if Some(trait_def_id) == self.tcx().lang_items().fn_mut_trait() {
let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref)
.find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
.unwrap();
fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref);
return;
} else if Some(trait_def_id) == self.tcx().lang_items().fn_trait() {
let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref)
.find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
.unwrap();
fn_traits.entry(super_trait_ref).or_default().fn_trait_ref = Some(trait_ref);
return;
}
}
// Otherwise, just group our traits and projection types.
traits.entry(trait_ref).or_default().extend(proj_ty);
}
fn pretty_print_bound_var(
&mut self,
debruijn: ty::DebruijnIndex,
@ -2553,3 +2682,10 @@ fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap<DefId, Symbol> {
pub fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers { trimmed_def_paths, ..*providers };
}
#[derive(Default)]
pub struct OpaqueFnEntry<'tcx> {
fn_mut_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
fn_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
return_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>,
}