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 all 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
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
Loading