Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve pretty printing for associated items in trait objects #120739

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
53 changes: 41 additions & 12 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,48 @@ 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);
.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_map(|proj| {
// Filter out projections that are implied by the super predicates.
let proj_is_implied = super_projections.iter().any(|&super_proj| {
let proj = cx.tcx().anonymize_bound_vars(proj);
let super_proj = cx.tcx().anonymize_bound_vars(super_proj);
assert_eq!(proj.bound_vars(), super_proj.bound_vars());
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved

let proj = proj.skip_binder();
let super_proj = ty::ExistentialProjection::erase_self_ty(
cx.tcx(),
super_proj.skip_binder(),
);

let mut projections: Vec<_> = predicates.projection_bounds().collect();
projections.sort_by_cached_key(|proj| {
cx.tcx().item_name(proj.item_def_id()).to_string()
});
proj == super_proj
});

// Skip the binder, because we don't want to print the binder in
// front of the associated item.
(!proj_is_implied).then_some(proj.skip_binder())
})
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
.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
31 changes: 31 additions & 0 deletions tests/ui/traits/object/pretty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Test for pretty-printing trait object types.

trait Super {
type Assoc;
}
trait Any: Super {}
trait Fixed: Super<Assoc = u8> {}
trait FixedSub: Fixed {}

trait SuperGeneric<'a> {
type Assoc;
}
trait AnyGeneric<'a>: SuperGeneric<'a> {}
trait FixedGeneric1<'a>: SuperGeneric<'a, Assoc = &'a u8> {}
trait FixedGeneric2<'a>: Super<Assoc = &'a u8> {}
trait FixedHrtb: for<'a> SuperGeneric<'a, Assoc = &'a 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_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc = &'a u8>) { x } //~ERROR mismatched types
fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc = &'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, Assoc = &u8>) { x } //~ERROR mismatched types
fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x } //~ERROR mismatched types

fn main() {}
124 changes: 124 additions & 0 deletions tests/ui/traits/object/pretty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
error[E0308]: mismatched types
--> $DIR/pretty.rs:18: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:19: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:20: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:21: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:22: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:24:74
|
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, Assoc = &'a u8>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> SuperGeneric<'a, Assoc = &'a u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:25:70
|
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, Assoc = &'a u8>`
|
= note: expected unit type `()`
found reference `&dyn for<'a> AnyGeneric<'a, Assoc = &'a u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:26: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:27: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:28:78
|
LL | fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc = &u8>) { x }
| - ^ expected `()`, found `&dyn FixedGeneric1<'a, Assoc = &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, Assoc = &u8>`

error[E0308]: mismatched types
--> $DIR/pretty.rs:29: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: aborting due to 11 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
8 changes: 4 additions & 4 deletions tests/ui/wf/hir-wf-canonicalized.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ help: this trait has no implementations, consider adding one
LL | trait Foo {
| ^^^^^^^^^

error[E0277]: the trait bound `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied
error[E0277]: the trait bound `(dyn Callback<Bar<'a, T>, Output = ()> + 'static): Foo` is not satisfied
--> $DIR/hir-wf-canonicalized.rs:10:15
|
LL | callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(dyn Callback<Bar<'a, T>, Output = ()> + 'static)`
|
help: this trait has no implementations, consider adding one
--> $DIR/hir-wf-canonicalized.rs:3:1
|
LL | trait Foo {
| ^^^^^^^^^

error[E0277]: the size for values of type `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time
error[E0277]: the size for values of type `(dyn Callback<Bar<'a, T>, Output = ()> + 'static)` cannot be known at compilation time
--> $DIR/hir-wf-canonicalized.rs:10:15
|
LL | callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)`
= help: the trait `Sized` is not implemented for `(dyn Callback<Bar<'a, T>, Output = ()> + 'static)`
note: required by an implicit `Sized` bound in `Bar`
--> $DIR/hir-wf-canonicalized.rs:9:16
|
Expand Down
Loading