mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 06:51:58 +00:00
Elaborate trait generics and associated types
This commit is contained in:
parent
c969b1dea0
commit
ce182d17f8
@ -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>>>,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user