Skip to content

Commit

Permalink
Rollup merge of #120739 - lukas-code:pp-dyn-assoc, r=compiler-errors
Browse files Browse the repository at this point in the history
improve pretty printing for associated items in trait objects

* Don't print a binder in front of associated items, because it's not valid syntax.
  * e.g. print `dyn for<'a> Trait<'a, Assoc = &'a u8>` instead of `dyn for<'a> Trait<'a, for<'a> Assoc = &'a u8>`.
* Don't print associated items that are implied by a supertrait bound.
  * e.g. if we have `trait Sub: Super<Assoc = u8> {}`, then just print `dyn Sub` instead of `dyn Sub<Assoc = u8>`.

I've added the test in the first commit, so you can see the diff of the compiler output in the second commit.
  • Loading branch information
matthiaskrgr authored Feb 7, 2024
2 parents e7e77b4 + c636c7a commit cd54a0d
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 39 deletions.
54 changes: 34 additions & 20 deletions compiler/rustc_middle/src/traits/util.rs
Original file line number Diff line number Diff line change
@@ -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<Item = Clause<'tcx>> {
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<Item = PolyTraitRef<'tcx>> {
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<PolyTraitRef<'tcx>>,
stack: Vec<PolyTraitRef<'tcx>>,
visited: FxHashSet<Clause<'tcx>>,
stack: Vec<Clause<'tcx>>,
}

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<PolyTraitRef<'tcx>> {
if let Some(trait_ref) = self.stack.pop() {
self.elaborate(trait_ref);
Some(trait_ref)
fn next(&mut self) -> Option<Clause<'tcx>> {
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
}
Expand Down
60 changes: 47 additions & 13 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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, &[]));

Expand All @@ -1281,19 +1281,53 @@ 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 bound_principal_with_self = bound_principal
.with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self);

let super_projections: Vec<_> =
super_predicates_for_pretty_printing(cx.tcx(), bound_principal_with_self)
.filter_map(|clause| clause.as_projection_clause())
.collect();

let mut projections: Vec<_> = predicates
.projection_bounds()
.filter(|&proj| {
// Filter out projections that are implied by the super predicates.
let proj_is_implied = super_projections.iter().any(|&super_proj| {
let super_proj = super_proj.map_bound(|super_proj| {
ty::ExistentialProjection::erase_self_ty(cx.tcx(), super_proj)
});

// This function is sometimes called on types with erased and
// anonymized regions, but the super projections can still
// contain named regions. So we erase and anonymize everything
// here to compare the types modulo regions below.
let proj = cx.tcx().erase_regions(proj);
let proj = cx.tcx().anonymize_bound_vars(proj);
let super_proj = cx.tcx().erase_regions(super_proj);
let super_proj = cx.tcx().anonymize_bound_vars(super_proj);

proj == super_proj
});
!proj_is_implied
})
.map(|proj| {
// Skip the binder, because we don't want to print the binder in
// front of the associated item.
proj.skip_binder()
})
.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| {
Expand Down
37 changes: 37 additions & 0 deletions tests/ui/traits/object/pretty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Test for pretty-printing trait object types.

trait Super {
type Assoc;
}
trait Any: Super {}
trait Fixed: Super<Assoc = u8> {}
trait FixedSub: Fixed {}
trait FixedStatic: Super<Assoc = &'static u8> {}

trait SuperGeneric<'a> {
type Assoc2;
}
trait AnyGeneric<'a>: SuperGeneric<'a> {}
trait FixedGeneric1<'a>: SuperGeneric<'a, Assoc2 = &'a u8> {}
trait FixedGeneric2<'a>: Super<Assoc = &'a u8> {}
trait FixedHrtb: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> {}
trait AnyDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super {}
trait FixedDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super<Assoc = u8> {}

fn dyn_super(x: &dyn Super<Assoc = u8>) { x } //~ERROR mismatched types
fn dyn_any(x: &dyn Any<Assoc = u8>) { x } //~ERROR mismatched types
fn dyn_fixed(x: &dyn Fixed) { x } //~ERROR mismatched types
fn dyn_fixed_multi(x: &dyn Fixed<Assoc = u16>) { x } //~ERROR mismatched types
fn dyn_fixed_sub(x: &dyn FixedSub) { x } //~ERROR mismatched types
fn dyn_fixed_static(x: &dyn FixedStatic) { x } //~ERROR mismatched types

fn dyn_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types
fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types
fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x } //~ERROR mismatched types
fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } //~ERROR mismatched types
fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>) { x } //~ERROR mismatched types
fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x } //~ERROR mismatched types
fn dyn_any_different_binders(x: &dyn AnyDifferentBinders<Assoc = u8>) { x } //~ERROR mismatched types
fn dyn_fixed_different_binders(x: &dyn FixedDifferentBinders) { x } //~ERROR mismatched types

fn main() {}
157 changes: 157 additions & 0 deletions tests/ui/traits/object/pretty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
error[E0308]: mismatched types
--> $DIR/pretty.rs:21:43
|
LL | fn dyn_super(x: &dyn Super<Assoc = u8>) { x }
| - ^ expected `()`, found `&dyn Super<Assoc = u8>`
| |
| help: try adding a return type: `-> &dyn Super<Assoc = u8>`
|
= note: expected unit type `()`
found reference `&dyn Super<Assoc = u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:22:39
|
LL | fn dyn_any(x: &dyn Any<Assoc = u8>) { x }
| - ^ expected `()`, found `&dyn Any<Assoc = u8>`
| |
| help: try adding a return type: `-> &dyn Any<Assoc = u8>`
|
= note: expected unit type `()`
found reference `&dyn Any<Assoc = u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:23:31
|
LL | fn dyn_fixed(x: &dyn Fixed) { x }
| - ^ expected `()`, found `&dyn Fixed`
| |
| help: try adding a return type: `-> &dyn Fixed`
|
= note: expected unit type `()`
found reference `&dyn Fixed`

error[E0308]: mismatched types
--> $DIR/pretty.rs:24:50
|
LL | fn dyn_fixed_multi(x: &dyn Fixed<Assoc = u16>) { x }
| - ^ expected `()`, found `&dyn Fixed<Assoc = u16>`
| |
| help: try adding a return type: `-> &dyn Fixed<Assoc = u16>`
|
= note: expected unit type `()`
found reference `&dyn Fixed<Assoc = u16>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:25:38
|
LL | fn dyn_fixed_sub(x: &dyn FixedSub) { x }
| - ^ expected `()`, found `&dyn FixedSub`
| |
| help: try adding a return type: `-> &dyn FixedSub`
|
= note: expected unit type `()`
found reference `&dyn FixedSub`

error[E0308]: mismatched types
--> $DIR/pretty.rs:26:44
|
LL | fn dyn_fixed_static(x: &dyn FixedStatic) { x }
| - ^ expected `()`, found `&dyn FixedStatic`
| |
| help: try adding a return type: `-> &dyn FixedStatic`
|
= note: expected unit type `()`
found reference `&dyn FixedStatic`

error[E0308]: mismatched types
--> $DIR/pretty.rs:28:75
|
LL | fn dyn_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>) { x }
| - ^ expected `()`, found `&dyn SuperGeneric<'a, Assoc2 = &u8>`
| |
| help: try adding a return type: `-> &dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:29:71
|
LL | fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>) { x }
| - ^ expected `()`, found `&dyn AnyGeneric<'a, Assoc2 = &u8>`
| |
| help: try adding a return type: `-> &dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:30:60
|
LL | fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x }
| - ^ expected `()`, found `&dyn FixedGeneric1<'a>`
| |
| help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> FixedGeneric1<'a>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:31:60
|
LL | fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x }
| - ^ expected `()`, found `&dyn FixedGeneric2<'a>`
| |
| help: try adding a return type: `-> &dyn for<'a> FixedGeneric2<'a>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> FixedGeneric2<'a>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:32:79
|
LL | fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>) { x }
| - ^ expected `()`, found `&dyn FixedGeneric1<'a, Assoc2 = ...>`
| |
| help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:33:40
|
LL | fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x }
| - ^ expected `()`, found `&dyn FixedHrtb`
| |
| help: try adding a return type: `-> &dyn FixedHrtb`
|
= note: expected unit type `()`
found reference `&dyn FixedHrtb`

error[E0308]: mismatched types
--> $DIR/pretty.rs:34:73
|
LL | fn dyn_any_different_binders(x: &dyn AnyDifferentBinders<Assoc = u8>) { x }
| - ^ expected `()`, found `&dyn AnyDifferentBinders<Assoc = ...>`
| |
| help: try adding a return type: `-> &dyn AnyDifferentBinders<Assoc = u8>`
|
= note: expected unit type `()`
found reference `&dyn AnyDifferentBinders<Assoc = u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:35:65
|
LL | fn dyn_fixed_different_binders(x: &dyn FixedDifferentBinders) { x }
| - ^ expected `()`, found `&dyn FixedDifferentBinders`
| |
| help: try adding a return type: `-> &dyn FixedDifferentBinders`
|
= note: expected unit type `()`
found reference `&dyn FixedDifferentBinders`

error: aborting due to 14 previous errors

For more information about this error, try `rustc --explain E0308`.
4 changes: 2 additions & 2 deletions tests/ui/wf/hir-wf-canonicalized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ trait Callback<T: Foo>: Fn(&Bar<'_, T>, &T::V) {}
struct Bar<'a, T> {
callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
//~^ ERROR the trait bound `Bar<'a, T>: Foo` is not satisfied
//~| ERROR the trait bound `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied
//~| ERROR the size for values of type `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time
//~| ERROR the trait bound `(dyn Callback<Bar<'a, T>, Output = ()> + 'static): Foo` is not satisfied
//~| ERROR the size for values of type `(dyn Callback<Bar<'a, T>, Output = ()> + 'static)` cannot be known at compilation time
}

impl<T: Foo> Bar<'_, Bar<'_, T>> {}
Expand Down
Loading

0 comments on commit cd54a0d

Please sign in to comment.