Fix printing impl trait under binders
This commit is contained in:
parent
fdca237d51
commit
20cea3ebb4
3 changed files with 189 additions and 124 deletions
|
@ -226,7 +226,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
value.as_ref().skip_binder().print(self)
|
value.as_ref().skip_binder().print(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_binder<T, F: Fn(&T, Self) -> Result<Self, fmt::Error>>(
|
fn wrap_binder<T, F: FnOnce(&T, Self) -> Result<Self, fmt::Error>>(
|
||||||
self,
|
self,
|
||||||
value: &ty::Binder<'tcx, T>,
|
value: &ty::Binder<'tcx, T>,
|
||||||
f: F,
|
f: F,
|
||||||
|
@ -773,18 +773,18 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
substs: &'tcx ty::List<ty::GenericArg<'tcx>>,
|
substs: &'tcx ty::List<ty::GenericArg<'tcx>>,
|
||||||
) -> Result<Self::Type, Self::Error> {
|
) -> Result<Self::Type, Self::Error> {
|
||||||
define_scoped_cx!(self);
|
let tcx = self.tcx();
|
||||||
|
|
||||||
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
|
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
|
||||||
// by looking up the projections associated with the def_id.
|
// by looking up the projections associated with the def_id.
|
||||||
let bounds = self.tcx().bound_explicit_item_bounds(def_id);
|
let bounds = tcx.bound_explicit_item_bounds(def_id);
|
||||||
|
|
||||||
let mut traits = FxIndexMap::default();
|
let mut traits = FxIndexMap::default();
|
||||||
let mut fn_traits = FxIndexMap::default();
|
let mut fn_traits = FxIndexMap::default();
|
||||||
let mut is_sized = false;
|
let mut is_sized = false;
|
||||||
|
|
||||||
for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
|
for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
|
||||||
let predicate = predicate.subst(self.tcx(), substs);
|
let predicate = predicate.subst(tcx, substs);
|
||||||
let bound_predicate = predicate.kind();
|
let bound_predicate = predicate.kind();
|
||||||
|
|
||||||
match bound_predicate.skip_binder() {
|
match bound_predicate.skip_binder() {
|
||||||
|
@ -792,7 +792,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
let trait_ref = bound_predicate.rebind(pred.trait_ref);
|
let trait_ref = bound_predicate.rebind(pred.trait_ref);
|
||||||
|
|
||||||
// Don't print + Sized, but rather + ?Sized if absent.
|
// Don't print + Sized, but rather + ?Sized if absent.
|
||||||
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
|
if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
|
||||||
is_sized = true;
|
is_sized = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -801,7 +801,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
}
|
}
|
||||||
ty::PredicateKind::Projection(pred) => {
|
ty::PredicateKind::Projection(pred) => {
|
||||||
let proj_ref = bound_predicate.rebind(pred);
|
let proj_ref = bound_predicate.rebind(pred);
|
||||||
let trait_ref = proj_ref.required_poly_trait_ref(self.tcx());
|
let trait_ref = proj_ref.required_poly_trait_ref(tcx);
|
||||||
|
|
||||||
// Projection type entry -- the def-id for naming, and the ty.
|
// Projection type entry -- the def-id for naming, and the ty.
|
||||||
let proj_ty = (proj_ref.projection_def_id(), proj_ref.term());
|
let proj_ty = (proj_ref.projection_def_id(), proj_ref.term());
|
||||||
|
@ -817,148 +817,168 @@ pub trait PrettyPrinter<'tcx>:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
define_scoped_cx!(self);
|
||||||
|
p!("impl ");
|
||||||
|
}
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
// Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait
|
// 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;
|
let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized;
|
||||||
|
|
||||||
p!("impl");
|
|
||||||
|
|
||||||
for (fn_once_trait_ref, entry) in fn_traits {
|
for (fn_once_trait_ref, entry) in fn_traits {
|
||||||
// Get the (single) generic ty (the args) of this FnOnce trait ref.
|
{
|
||||||
let generics = self.tcx().generics_of(fn_once_trait_ref.def_id());
|
define_scoped_cx!(self);
|
||||||
let args =
|
p!(
|
||||||
generics.own_substs_no_defaults(self.tcx(), fn_once_trait_ref.skip_binder().substs);
|
write("{}", if first { "" } else { " + " }),
|
||||||
|
write("{}", if paren_needed { "(" } else { "" })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
match (entry.return_ty, args[0].expect_ty()) {
|
self = self.wrap_binder(&fn_once_trait_ref, |trait_ref, mut self_| {
|
||||||
// We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded
|
// Get the (single) generic ty (the args) of this FnOnce trait ref.
|
||||||
// a return type.
|
let generics = tcx.generics_of(trait_ref.def_id);
|
||||||
(Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => {
|
let args = generics.own_substs_no_defaults(tcx, trait_ref.substs);
|
||||||
let name = if entry.fn_trait_ref.is_some() {
|
|
||||||
"Fn"
|
|
||||||
} else if entry.fn_mut_trait_ref.is_some() {
|
|
||||||
"FnMut"
|
|
||||||
} else {
|
|
||||||
"FnOnce"
|
|
||||||
};
|
|
||||||
|
|
||||||
p!(
|
match (entry.return_ty, args[0].expect_ty()) {
|
||||||
write("{}", if first { " " } else { " + " }),
|
// We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded
|
||||||
write("{}{}(", if paren_needed { "(" } else { "" }, name)
|
// 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"
|
||||||
|
};
|
||||||
|
|
||||||
for (idx, ty) in arg_tys.tuple_fields().iter().enumerate() {
|
define_scoped_cx!(self_);
|
||||||
if idx > 0 {
|
p!(write("{}(", name));
|
||||||
|
|
||||||
|
for (idx, ty) in arg_tys.tuple_fields().iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
p!(", ");
|
||||||
|
}
|
||||||
|
p!(print(ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
p!(")");
|
||||||
|
if let Term::Ty(ty) = return_ty.skip_binder() {
|
||||||
|
if !ty.is_unit() {
|
||||||
|
p!(" -> ", print(return_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p!(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.
|
||||||
|
_ => {
|
||||||
|
if entry.has_fn_once {
|
||||||
|
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| (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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self_)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the rest of the trait types (that aren't Fn* family of traits)
|
||||||
|
for (trait_ref, assoc_items) in traits {
|
||||||
|
{
|
||||||
|
define_scoped_cx!(self);
|
||||||
|
p!(write("{}", if first { "" } else { " + " }));
|
||||||
|
}
|
||||||
|
|
||||||
|
self = self.wrap_binder(&trait_ref, |trait_ref, mut self_| {
|
||||||
|
define_scoped_cx!(self_);
|
||||||
|
p!(print(trait_ref.print_only_trait_name()));
|
||||||
|
|
||||||
|
let generics = tcx.generics_of(trait_ref.def_id);
|
||||||
|
let args = generics.own_substs_no_defaults(tcx, trait_ref.substs);
|
||||||
|
|
||||||
|
if !args.is_empty() || !assoc_items.is_empty() {
|
||||||
|
let mut first = true;
|
||||||
|
|
||||||
|
for ty in args {
|
||||||
|
if first {
|
||||||
|
p!("<");
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
p!(", ");
|
p!(", ");
|
||||||
}
|
}
|
||||||
p!(print(ty));
|
p!(print(ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
p!(")");
|
for (assoc_item_def_id, term) in assoc_items {
|
||||||
if let Term::Ty(ty) = return_ty.skip_binder() {
|
// Skip printing `<[generator@] as Generator<_>>::Return` from async blocks,
|
||||||
if !ty.is_unit() {
|
// unless we can find out what generator return type it comes from.
|
||||||
p!(" -> ", print(return_ty));
|
let term = if let Some(ty) = term.skip_binder().ty()
|
||||||
}
|
&& let ty::Projection(ty::ProjectionTy { item_def_id, substs }) = ty.kind()
|
||||||
}
|
&& Some(*item_def_id) == tcx.lang_items().generator_return()
|
||||||
p!(write("{}", if paren_needed { ")" } else { "" }));
|
{
|
||||||
|
if let ty::Generator(_, substs, _) = substs.type_at(0).kind() {
|
||||||
first = false;
|
let return_ty = substs.as_generator().return_ty();
|
||||||
}
|
if !return_ty.is_ty_infer() {
|
||||||
// If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the
|
return_ty.into()
|
||||||
// trait_refs we collected in the OpaqueFnEntry as normal trait refs.
|
} else {
|
||||||
_ => {
|
continue;
|
||||||
if entry.has_fn_once {
|
}
|
||||||
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.tcx().generics_of(trait_ref.def_id());
|
|
||||||
let args = generics.own_substs_no_defaults(self.tcx(), trait_ref.skip_binder().substs);
|
|
||||||
|
|
||||||
if !args.is_empty() || !assoc_items.is_empty() {
|
|
||||||
let mut first = true;
|
|
||||||
|
|
||||||
for ty in args {
|
|
||||||
if first {
|
|
||||||
p!("<");
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
p!(", ");
|
|
||||||
}
|
|
||||||
p!(print(trait_ref.rebind(*ty)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (assoc_item_def_id, term) in assoc_items {
|
|
||||||
// Skip printing `<[generator@] as Generator<_>>::Return` from async blocks,
|
|
||||||
// unless we can find out what generator return type it comes from.
|
|
||||||
let term = if let Some(ty) = term.skip_binder().ty()
|
|
||||||
&& let ty::Projection(ty::ProjectionTy { item_def_id, substs }) = ty.kind()
|
|
||||||
&& Some(*item_def_id) == self.tcx().lang_items().generator_return()
|
|
||||||
{
|
|
||||||
if let ty::Generator(_, substs, _) = substs.type_at(0).kind() {
|
|
||||||
let return_ty = substs.as_generator().return_ty();
|
|
||||||
if !return_ty.is_ty_infer() {
|
|
||||||
return_ty.into()
|
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
continue;
|
term.skip_binder()
|
||||||
}
|
};
|
||||||
} else {
|
|
||||||
term.skip_binder()
|
|
||||||
};
|
|
||||||
|
|
||||||
if first {
|
if first {
|
||||||
p!("<");
|
p!("<");
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
p!(", ");
|
p!(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name));
|
||||||
|
|
||||||
|
match term {
|
||||||
|
Term::Ty(ty) => {
|
||||||
|
p!(print(ty))
|
||||||
|
}
|
||||||
|
Term::Const(c) => {
|
||||||
|
p!(print(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
p!(write("{} = ", self.tcx().associated_item(assoc_item_def_id).name));
|
if !first {
|
||||||
|
p!(">");
|
||||||
match term {
|
}
|
||||||
Term::Ty(ty) => {
|
|
||||||
p!(print(ty))
|
|
||||||
}
|
|
||||||
Term::Const(c) => {
|
|
||||||
p!(print(c));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !first {
|
first = false;
|
||||||
p!(">");
|
Ok(self_)
|
||||||
}
|
})?;
|
||||||
}
|
|
||||||
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define_scoped_cx!(self);
|
||||||
|
|
||||||
if !is_sized {
|
if !is_sized {
|
||||||
p!(write("{}?Sized", if first { " " } else { " + " }));
|
p!(write("{}?Sized", if first { "" } else { " + " }));
|
||||||
} else if first {
|
} else if first {
|
||||||
p!(" Sized");
|
p!("Sized");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -1869,7 +1889,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
||||||
self.pretty_in_binder(value)
|
self.pretty_in_binder(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_binder<T, C: Fn(&T, Self) -> Result<Self, Self::Error>>(
|
fn wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, Self::Error>>(
|
||||||
self,
|
self,
|
||||||
value: &ty::Binder<'tcx, T>,
|
value: &ty::Binder<'tcx, T>,
|
||||||
f: C,
|
f: C,
|
||||||
|
@ -2256,7 +2276,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
||||||
Ok(inner)
|
Ok(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pretty_wrap_binder<T, C: Fn(&T, Self) -> Result<Self, fmt::Error>>(
|
pub fn pretty_wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, fmt::Error>>(
|
||||||
self,
|
self,
|
||||||
value: &ty::Binder<'tcx, T>,
|
value: &ty::Binder<'tcx, T>,
|
||||||
f: C,
|
f: C,
|
||||||
|
|
14
src/test/ui/impl-trait/printing-binder.rs
Normal file
14
src/test/ui/impl-trait/printing-binder.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
trait Trait<'a> {}
|
||||||
|
impl<T> Trait<'_> for T {}
|
||||||
|
fn whatever() -> impl for<'a> Trait<'a> + for<'b> Trait<'b> {}
|
||||||
|
|
||||||
|
fn whatever2() -> impl for<'c> Fn(&'c ()) {
|
||||||
|
|_: &()| {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: u32 = whatever();
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
let x2: u32 = whatever2();
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
}
|
31
src/test/ui/impl-trait/printing-binder.stderr
Normal file
31
src/test/ui/impl-trait/printing-binder.stderr
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/printing-binder.rs:10:18
|
||||||
|
|
|
||||||
|
LL | fn whatever() -> impl for<'a> Trait<'a> + for<'b> Trait<'b> {}
|
||||||
|
| ------------------------------------------ the found opaque type
|
||||||
|
...
|
||||||
|
LL | let x: u32 = whatever();
|
||||||
|
| --- ^^^^^^^^^^ expected `u32`, found opaque type
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: expected type `u32`
|
||||||
|
found opaque type `impl for<'a> Trait<'a> + for<'b> Trait<'b>`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/printing-binder.rs:12:19
|
||||||
|
|
|
||||||
|
LL | fn whatever2() -> impl for<'c> Fn(&'c ()) {
|
||||||
|
| ----------------------- the found opaque type
|
||||||
|
...
|
||||||
|
LL | let x2: u32 = whatever2();
|
||||||
|
| --- ^^^^^^^^^^^ expected `u32`, found opaque type
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: expected type `u32`
|
||||||
|
found opaque type `impl for<'c> Fn(&'c ())`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Add a link
Reference in a new issue