From d476b76e12d3e1d2916392083e6e44229cfa4869 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Tue, 6 Feb 2024 22:37:47 +0100 Subject: [PATCH] improve pretty printing for trait objects --- compiler/rustc_middle/src/traits/util.rs | 54 ++++++++++++-------- compiler/rustc_middle/src/ty/print/pretty.rs | 50 +++++++++++++----- tests/ui/traits/object/pretty.stderr | 42 +++++++-------- tests/ui/wf/hir-wf-canonicalized.rs | 4 +- tests/ui/wf/hir-wf-canonicalized.stderr | 8 +-- 5 files changed, 98 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs index b4054f8ff5edd..fd5302dc75b13 100644 --- a/compiler/rustc_middle/src/traits/util.rs +++ b/compiler/rustc_middle/src/traits/util.rs @@ -1,46 +1,60 @@ use rustc_data_structures::fx::FxHashSet; -use crate::ty::{PolyTraitRef, TyCtxt}; +use crate::ty::{Clause, PolyTraitRef, ToPolyTraitRef, ToPredicate, TyCtxt}; -/// Given a PolyTraitRef, get the PolyTraitRefs of the trait's (transitive) supertraits. +/// Given a [`PolyTraitRef`], get the [`Clause`]s implied by the trait's definition. +/// /// This only exists in `rustc_middle` because the more powerful elaborator depends on /// `rustc_infer` for elaborating outlives bounds -- this should only be used for pretty /// printing. +pub fn super_predicates_for_pretty_printing<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: PolyTraitRef<'tcx>, +) -> impl Iterator> { + let clause = trait_ref.to_predicate(tcx); + Elaborator { tcx, visited: FxHashSet::from_iter([clause]), stack: vec![clause] } +} + +/// Like [`super_predicates_for_pretty_printing`], except it only returns traits and filters out +/// all other [`Clause`]s. pub fn supertraits_for_pretty_printing<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: PolyTraitRef<'tcx>, ) -> impl Iterator> { - Elaborator { tcx, visited: FxHashSet::from_iter([trait_ref]), stack: vec![trait_ref] } + super_predicates_for_pretty_printing(tcx, trait_ref).filter_map(|clause| { + clause.as_trait_clause().map(|trait_clause| trait_clause.to_poly_trait_ref()) + }) } struct Elaborator<'tcx> { tcx: TyCtxt<'tcx>, - visited: FxHashSet>, - stack: Vec>, + visited: FxHashSet>, + stack: Vec>, } impl<'tcx> Elaborator<'tcx> { fn elaborate(&mut self, trait_ref: PolyTraitRef<'tcx>) { - let supertrait_refs = self - .tcx - .super_predicates_of(trait_ref.def_id()) - .predicates - .into_iter() - .flat_map(|(pred, _)| pred.subst_supertrait(self.tcx, &trait_ref).as_trait_clause()) - .map(|t| t.map_bound(|pred| pred.trait_ref)) - .filter(|supertrait_ref| self.visited.insert(*supertrait_ref)); - - self.stack.extend(supertrait_refs); + let super_predicates = + self.tcx.super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map( + |&(pred, _)| { + let clause = pred.subst_supertrait(self.tcx, &trait_ref); + self.visited.insert(clause).then_some(clause) + }, + ); + + self.stack.extend(super_predicates); } } impl<'tcx> Iterator for Elaborator<'tcx> { - type Item = PolyTraitRef<'tcx>; + type Item = Clause<'tcx>; - fn next(&mut self) -> Option> { - if let Some(trait_ref) = self.stack.pop() { - self.elaborate(trait_ref); - Some(trait_ref) + fn next(&mut self) -> Option> { + if let Some(clause) = self.stack.pop() { + if let Some(trait_clause) = clause.as_trait_clause() { + self.elaborate(trait_clause.to_poly_trait_ref()); + } + Some(clause) } else { None } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c0bfd2380ade0..e1ea52efa3d55 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,7 +1,7 @@ use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::query::IntoQueryParam; use crate::query::Providers; -use crate::traits::util::supertraits_for_pretty_printing; +use crate::traits::util::{super_predicates_for_pretty_printing, supertraits_for_pretty_printing}; use crate::ty::GenericArgKind; use crate::ty::{ ConstInt, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, @@ -1255,8 +1255,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // Generate the main trait ref, including associated types. let mut first = true; - if let Some(principal) = predicates.principal() { - self.wrap_binder(&principal, |principal, cx| { + if let Some(bound_principal) = predicates.principal() { + self.wrap_binder(&bound_principal, |principal, cx| { define_scoped_cx!(cx); p!(print_def_path(principal.def_id, &[])); @@ -1281,19 +1281,43 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, // in order to place the projections inside the `<...>`. if !resugared { - // Use a type that can't appear in defaults of type parameters. - let dummy_cx = Ty::new_fresh(cx.tcx(), 0); - let principal = principal.with_self_ty(cx.tcx(), dummy_cx); + let principal_with_self = + principal.with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self); let args = cx .tcx() - .generics_of(principal.def_id) - .own_args_no_defaults(cx.tcx(), principal.args); - - let mut projections: Vec<_> = predicates.projection_bounds().collect(); - projections.sort_by_cached_key(|proj| { - cx.tcx().item_name(proj.item_def_id()).to_string() - }); + .generics_of(principal_with_self.def_id) + .own_args_no_defaults(cx.tcx(), principal_with_self.args); + + let super_projections = super_predicates_for_pretty_printing( + cx.tcx(), + bound_principal.rebind(principal_with_self), + ) + .filter_map(|clause| clause.as_projection_clause()) + .collect::>(); + + let mut projections: Vec<_> = predicates + .projection_bounds() + .filter_map(|proj| { + // Skip the binder, because we don't want to print the binder in + // front of the associated item. + let proj = proj.skip_binder(); + // Filter out projections that are implied by the super predicates. + super_projections + .iter() + .all(|super_proj| { + let super_proj = ty::ExistentialProjection::erase_self_ty( + cx.tcx(), + super_proj.skip_binder(), + ); + proj != super_proj + }) + .then_some(proj) + }) + .collect(); + + projections + .sort_by_cached_key(|proj| cx.tcx().item_name(proj.def_id).to_string()); if !args.is_empty() || !projections.is_empty() { p!(generic_delimiters(|cx| { diff --git a/tests/ui/traits/object/pretty.stderr b/tests/ui/traits/object/pretty.stderr index 2fa87e7a6fe54..7b16177907e6a 100644 --- a/tests/ui/traits/object/pretty.stderr +++ b/tests/ui/traits/object/pretty.stderr @@ -24,34 +24,34 @@ error[E0308]: mismatched types --> $DIR/pretty.rs:20:31 | LL | fn dyn_fixed(x: &dyn Fixed) { x } - | - ^ expected `()`, found `&dyn Fixed` + | - ^ expected `()`, found `&dyn Fixed` | | - | help: try adding a return type: `-> &dyn Fixed` + | help: try adding a return type: `-> &dyn Fixed` | = note: expected unit type `()` - found reference `&dyn Fixed` + found reference `&dyn Fixed` error[E0308]: mismatched types --> $DIR/pretty.rs:21:50 | LL | fn dyn_fixed_multi(x: &dyn Fixed) { x } - | - ^ expected `()`, found `&dyn Fixed` + | - ^ expected `()`, found `&dyn Fixed` | | - | help: try adding a return type: `-> &dyn Fixed` + | help: try adding a return type: `-> &dyn Fixed` | = note: expected unit type `()` - found reference `&dyn Fixed` + found reference `&dyn Fixed` error[E0308]: mismatched types --> $DIR/pretty.rs:22:38 | LL | fn dyn_fixed_sub(x: &dyn FixedSub) { x } - | - ^ expected `()`, found `&dyn FixedSub` + | - ^ expected `()`, found `&dyn FixedSub` | | - | help: try adding a return type: `-> &dyn FixedSub` + | help: try adding a return type: `-> &dyn FixedSub` | = note: expected unit type `()` - found reference `&dyn FixedSub` + found reference `&dyn FixedSub` error[E0308]: mismatched types --> $DIR/pretty.rs:24:74 @@ -59,10 +59,10 @@ error[E0308]: mismatched types LL | fn dyn_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc = &'a u8>) { x } | - ^ expected `()`, found `&dyn SuperGeneric<'a, Assoc = &u8>` | | - | help: try adding a return type: `-> &dyn for<'a> SuperGeneric<'a, for<'a> Assoc = &'a u8>` + | help: try adding a return type: `-> &dyn for<'a> SuperGeneric<'a, Assoc = &'a u8>` | = note: expected unit type `()` - found reference `&dyn for<'a> SuperGeneric<'a, for<'a> Assoc = &'a u8>` + found reference `&dyn for<'a> SuperGeneric<'a, Assoc = &'a u8>` error[E0308]: mismatched types --> $DIR/pretty.rs:25:70 @@ -70,10 +70,10 @@ error[E0308]: mismatched types LL | fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc = &'a u8>) { x } | - ^ expected `()`, found `&dyn AnyGeneric<'a, Assoc = &u8>` | | - | help: try adding a return type: `-> &dyn for<'a> AnyGeneric<'a, for<'a> Assoc = &'a u8>` + | help: try adding a return type: `-> &dyn for<'a> AnyGeneric<'a, Assoc = &'a u8>` | = note: expected unit type `()` - found reference `&dyn for<'a> AnyGeneric<'a, for<'a> Assoc = &'a u8>` + found reference `&dyn for<'a> AnyGeneric<'a, Assoc = &'a u8>` error[E0308]: mismatched types --> $DIR/pretty.rs:26:60 @@ -81,10 +81,10 @@ error[E0308]: mismatched types LL | fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x } | - ^ expected `()`, found `&dyn FixedGeneric1<'a, Assoc = &u8>` | | - | help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a, for<'a> Assoc = &'a u8>` + | help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a>` | = note: expected unit type `()` - found reference `&dyn for<'a> FixedGeneric1<'a, for<'a> Assoc = &'a u8>` + found reference `&dyn for<'a> FixedGeneric1<'a>` error[E0308]: mismatched types --> $DIR/pretty.rs:27:60 @@ -92,10 +92,10 @@ error[E0308]: mismatched types LL | fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } | - ^ expected `()`, found `&dyn FixedGeneric2<'a, Assoc = &u8>` | | - | help: try adding a return type: `-> &dyn for<'a> FixedGeneric2<'a, for<'a> Assoc = &'a u8>` + | help: try adding a return type: `-> &dyn for<'a> FixedGeneric2<'a>` | = note: expected unit type `()` - found reference `&dyn for<'a> FixedGeneric2<'a, for<'a> Assoc = &'a u8>` + found reference `&dyn for<'a> FixedGeneric2<'a>` error[E0308]: mismatched types --> $DIR/pretty.rs:28:78 @@ -103,10 +103,10 @@ error[E0308]: mismatched types LL | fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc = &u8>) { x } | - ^ expected `()`, found `&dyn FixedGeneric1<'a, Assoc = ..., Assoc = ...>` | | - | help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a, for<'a> Assoc = &u8, for<'a> Assoc = &'a u8>` + | help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a, Assoc = &u8>` | = note: expected unit type `()` - found reference `&dyn for<'a> FixedGeneric1<'a, for<'a> Assoc = &u8, for<'a> Assoc = &'a u8>` + found reference `&dyn for<'a> FixedGeneric1<'a, Assoc = &u8>` error[E0308]: mismatched types --> $DIR/pretty.rs:29:40 @@ -114,10 +114,10 @@ error[E0308]: mismatched types LL | fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x } | - ^ expected `()`, found `&dyn FixedHrtb` | | - | help: try adding a return type: `-> &dyn FixedHrtb Assoc = &'a u8>` + | help: try adding a return type: `-> &dyn FixedHrtb` | = note: expected unit type `()` - found reference `&dyn FixedHrtb Assoc = &'a u8>` + found reference `&dyn FixedHrtb` error: aborting due to 11 previous errors diff --git a/tests/ui/wf/hir-wf-canonicalized.rs b/tests/ui/wf/hir-wf-canonicalized.rs index bdb84409d009d..eac238f0fcab1 100644 --- a/tests/ui/wf/hir-wf-canonicalized.rs +++ b/tests/ui/wf/hir-wf-canonicalized.rs @@ -9,8 +9,8 @@ trait Callback: Fn(&Bar<'_, T>, &T::V) {} struct Bar<'a, T> { callback: Box>>>, //~^ ERROR the trait bound `Bar<'a, T>: Foo` is not satisfied - //~| ERROR the trait bound `(dyn Callback, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied - //~| ERROR the size for values of type `(dyn Callback, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time + //~| ERROR the trait bound `(dyn Callback, Output = ()> + 'static): Foo` is not satisfied + //~| ERROR the size for values of type `(dyn Callback, Output = ()> + 'static)` cannot be known at compilation time } impl Bar<'_, Bar<'_, T>> {} diff --git a/tests/ui/wf/hir-wf-canonicalized.stderr b/tests/ui/wf/hir-wf-canonicalized.stderr index 4dca1f65232e2..8938801ce3d97 100644 --- a/tests/ui/wf/hir-wf-canonicalized.stderr +++ b/tests/ui/wf/hir-wf-canonicalized.stderr @@ -10,11 +10,11 @@ help: this trait has no implementations, consider adding one LL | trait Foo { | ^^^^^^^^^ -error[E0277]: the trait bound `(dyn Callback, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied +error[E0277]: the trait bound `(dyn Callback, Output = ()> + 'static): Foo` is not satisfied --> $DIR/hir-wf-canonicalized.rs:10:15 | LL | callback: Box>>>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(dyn Callback, for<'b, 'c, 'd> Output = ()> + 'static)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(dyn Callback, Output = ()> + 'static)` | help: this trait has no implementations, consider adding one --> $DIR/hir-wf-canonicalized.rs:3:1 @@ -22,13 +22,13 @@ help: this trait has no implementations, consider adding one LL | trait Foo { | ^^^^^^^^^ -error[E0277]: the size for values of type `(dyn Callback, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Callback, Output = ()> + 'static)` cannot be known at compilation time --> $DIR/hir-wf-canonicalized.rs:10:15 | LL | callback: Box>>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `Sized` is not implemented for `(dyn Callback, for<'b, 'c, 'd> Output = ()> + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Callback, Output = ()> + 'static)` note: required by an implicit `Sized` bound in `Bar` --> $DIR/hir-wf-canonicalized.rs:9:16 |