Rollup merge of #98371 - compiler-errors:better-opaque-printing, r=oli-obk

Fix printing `impl trait` under binders

Before, we would render `impl for<'a> Trait<'a>` like `impl Trait<for<'a> 'a>`, lol.
This commit is contained in:
Yuki Okushi 2022-06-26 13:14:56 +09:00 committed by GitHub
commit 645e5c475a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 177 additions and 125 deletions

View File

@ -226,7 +226,7 @@ pub trait PrettyPrinter<'tcx>:
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,
value: &ty::Binder<'tcx, T>,
f: F,
@ -773,18 +773,18 @@ pub trait PrettyPrinter<'tcx>:
def_id: DefId,
substs: &'tcx ty::List<ty::GenericArg<'tcx>>,
) -> Result<Self::Type, Self::Error> {
define_scoped_cx!(self);
let tcx = self.tcx();
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
// 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 fn_traits = FxIndexMap::default();
let mut is_sized = false;
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();
match bound_predicate.skip_binder() {
@ -792,7 +792,7 @@ pub trait PrettyPrinter<'tcx>:
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() {
if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
is_sized = true;
continue;
}
@ -801,7 +801,7 @@ pub trait PrettyPrinter<'tcx>:
}
ty::PredicateKind::Projection(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.
let proj_ty = (proj_ref.projection_def_id(), proj_ref.term());
@ -817,148 +817,155 @@ pub trait PrettyPrinter<'tcx>:
}
}
write!(self, "impl ")?;
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.tcx().generics_of(fn_once_trait_ref.def_id());
let args =
generics.own_substs_no_defaults(self.tcx(), fn_once_trait_ref.skip_binder().substs);
write!(self, "{}", if first { "" } else { " + " })?;
write!(self, "{}", if paren_needed { "(" } else { "" })?;
match (entry.return_ty, args[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"
};
self = self.wrap_binder(&fn_once_trait_ref, |trait_ref, mut cx| {
define_scoped_cx!(cx);
// Get the (single) generic ty (the args) of this FnOnce trait ref.
let generics = tcx.generics_of(trait_ref.def_id);
let args = generics.own_substs_no_defaults(tcx, trait_ref.substs);
p!(
write("{}", if first { " " } else { " + " }),
write("{}{}(", if paren_needed { "(" } else { "" }, name)
);
match (entry.return_ty, args[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"
};
for (idx, ty) in arg_tys.tuple_fields().iter().enumerate() {
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(cx)
})?;
}
// Print the rest of the trait types (that aren't Fn* family of traits)
for (trait_ref, assoc_items) in traits {
write!(self, "{}", if first { "" } else { " + " })?;
self = self.wrap_binder(&trait_ref, |trait_ref, mut cx| {
define_scoped_cx!(cx);
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!(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| (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()
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) == 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 {
continue;
}
} else {
continue;
}
} else {
continue;
}
} else {
term.skip_binder()
};
term.skip_binder()
};
if first {
p!("<");
first = false;
} else {
p!(", ");
if first {
p!("<");
first = false;
} else {
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));
match term {
Term::Ty(ty) => {
p!(print(ty))
}
Term::Const(c) => {
p!(print(c));
}
};
if !first {
p!(">");
}
}
if !first {
p!(">");
}
}
first = false;
first = false;
Ok(cx)
})?;
}
if !is_sized {
p!(write("{}?Sized", if first { " " } else { " + " }));
write!(self, "{}?Sized", if first { "" } else { " + " })?;
} else if first {
p!(" Sized");
write!(self, "Sized")?;
}
Ok(self)
@ -1869,7 +1876,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
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,
value: &ty::Binder<'tcx, T>,
f: C,
@ -2256,7 +2263,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
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,
value: &ty::Binder<'tcx, T>,
f: C,

View File

@ -22,7 +22,7 @@ LL | async fn baz<T>(_c: impl FnMut() -> T) where T: Future<Output=()> {
LL | |
LL | | }
| |_^
= note: required because it captures the following types: `ResumeTy`, `impl Future<Output = ()>`, `()`
= note: required because it captures the following types: `ResumeTy`, `impl for<'r, 's, 't0> Future<Output = ()>`, `()`
note: required because it's used within this `async` block
--> $DIR/issue-70935-complex-spans.rs:23:16
|

View 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
}

View 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`.