Skip to content

Commit

Permalink
rustc_middle: Pretty-print negative bounds correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Dec 27, 2023
1 parent 7f4a1a4 commit 255a0e9
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 22 deletions.
77 changes: 55 additions & 22 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,23 +912,43 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

let mut traits = FxIndexMap::default();
let mut fn_traits = FxIndexMap::default();
let mut is_sized = false;
let mut sizedness = Sizedness::Maybe;
let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new();

enum Sizedness {
Positive,
Negative,
Maybe,
}

for (predicate, _) in bounds.iter_instantiated_copied(tcx, args) {
let bound_predicate = predicate.kind();

match bound_predicate.skip_binder() {
ty::ClauseKind::Trait(pred) => {
let trait_ref = bound_predicate.rebind(pred.trait_ref);

// Don't print + Sized, but rather + ?Sized if absent.
// Don't print `+ Sized`, but rather `+ ?Sized` if absent.
if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
is_sized = true;
continue;
match pred.polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {
sizedness = Sizedness::Positive;
continue;
}
ty::ImplPolarity::Negative if let Sizedness::Maybe = &sizedness => {
sizedness = Sizedness::Negative;
}
_ => {}
}
}

self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits);
self.insert_trait_and_projection(
trait_ref,
pred.polarity,
None,
&mut traits,
&mut fn_traits,
);
}
ty::ClauseKind::Projection(pred) => {
let proj_ref = bound_predicate.rebind(pred);
Expand All @@ -939,6 +959,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

self.insert_trait_and_projection(
trait_ref,
ty::ImplPolarity::Positive,
Some(proj_ty),
&mut traits,
&mut fn_traits,
Expand All @@ -955,7 +976,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

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;
let paren_needed =
fn_traits.len() > 1 || traits.len() > 0 || matches!(sizedness, Sizedness::Maybe);

for (fn_once_trait_ref, entry) in fn_traits {
write!(self, "{}", if first { "" } else { " + " })?;
Expand Down Expand Up @@ -1002,18 +1024,21 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
// 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.require_lang_item(LangItem::FnOnce, None), ty)),
);
traits
.entry((fn_once_trait_ref, ty::ImplPolarity::Positive))
.or_default()
.extend(
// Group the return ty with its def id, if we had one.
entry.return_ty.map(|ty| {
(tcx.require_lang_item(LangItem::FnOnce, None), ty)
}),
);
}
if let Some(trait_ref) = entry.fn_mut_trait_ref {
traits.entry(trait_ref).or_default();
traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default();
}
if let Some(trait_ref) = entry.fn_trait_ref {
traits.entry(trait_ref).or_default();
traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default();
}
}
}
Expand All @@ -1023,11 +1048,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}

// Print the rest of the trait types (that aren't Fn* family of traits)
for (trait_ref, assoc_items) in traits {
for ((trait_ref, polarity), assoc_items) in traits {
write!(self, "{}", if first { "" } else { " + " })?;

self.wrap_binder(&trait_ref, |trait_ref, cx| {
define_scoped_cx!(cx);

if polarity == ty::ImplPolarity::Negative {
p!("!");
}
p!(print(trait_ref.print_only_trait_name()));

let generics = tcx.generics_of(trait_ref.def_id);
Expand Down Expand Up @@ -1094,10 +1123,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
})?;
}

if !is_sized {
write!(self, "{}?Sized", if first { "" } else { " + " })?;
} else if first {
write!(self, "Sized")?;
match sizedness {
Sizedness::Positive if first => write!(self, "Sized")?,
Sizedness::Maybe => write!(self, "{}?Sized", if first { "" } else { " + " })?,
_ => {}
}

if !with_forced_trimmed_paths() {
Expand Down Expand Up @@ -1128,9 +1157,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
fn insert_trait_and_projection(
&mut self,
trait_ref: ty::PolyTraitRef<'tcx>,
polarity: ty::ImplPolarity,
proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>,
traits: &mut FxIndexMap<
ty::PolyTraitRef<'tcx>,
(ty::PolyTraitRef<'tcx>, ty::ImplPolarity),
FxIndexMap<DefId, ty::Binder<'tcx, Term<'tcx>>>,
>,
fn_traits: &mut FxIndexMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
Expand All @@ -1139,7 +1169,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {

// If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce
// super-trait ref and record it there.
if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() {
// We skip negative Fn* bounds since they can't use parenthetical notation anyway.
if polarity == ty::ImplPolarity::Positive
&& let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait()
{
// If we have a FnOnce, then insert it into
if trait_def_id == fn_once_trait {
let entry = fn_traits.entry(trait_ref).or_default();
Expand Down Expand Up @@ -1167,7 +1200,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}

// Otherwise, just group our traits and projection types.
traits.entry(trait_ref).or_default().extend(proj_ty);
traits.entry((trait_ref, polarity)).or_default().extend(proj_ty);
}

fn pretty_print_inherent_projection(
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// compile-flags: -Znext-solver

#![feature(negative_bounds, negative_impls)]
//~^ WARN the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes

trait Trait {}
impl !Trait for () {}

fn produce() -> impl !Trait {}
fn consume(_: impl Trait) {}

fn main() {
consume(produce()); //~ ERROR the trait bound `impl !Trait: Trait` is not satisfied
}

fn weird0() -> impl Sized + !Sized {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Sized`
fn weird1() -> impl Sized + !Sized {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Sized`
fn weird2() -> impl !Sized + Sized {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Sized`
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
warning: the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/opaque-type-unsatisfied-bound.rs:3:12
|
LL | #![feature(negative_bounds, negative_impls)]
| ^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/opaque-type-unsatisfied-bound.rs:16:36
|
LL | fn weird0() -> impl Sized + !Sized {}
| ------------------- ^^ types differ
| |
| the expected opaque type
|
= note: expected opaque type `impl !Sized`
found unit type `()`

error[E0271]: type mismatch resolving `() == impl !Sized`
--> $DIR/opaque-type-unsatisfied-bound.rs:16:16
|
LL | fn weird0() -> impl Sized + !Sized {}
| ^^^^^^^^^^^^^^^^^^^ types differ

error[E0308]: mismatched types
--> $DIR/opaque-type-unsatisfied-bound.rs:19:36
|
LL | fn weird1() -> impl Sized + !Sized {}
| ------------------- ^^ types differ
| |
| the expected opaque type
|
= note: expected opaque type `impl !Sized`
found unit type `()`

error[E0271]: type mismatch resolving `() == impl !Sized`
--> $DIR/opaque-type-unsatisfied-bound.rs:19:16
|
LL | fn weird1() -> impl Sized + !Sized {}
| ^^^^^^^^^^^^^^^^^^^ types differ

error[E0308]: mismatched types
--> $DIR/opaque-type-unsatisfied-bound.rs:22:36
|
LL | fn weird2() -> impl !Sized + Sized {}
| ------------------- ^^ types differ
| |
| the expected opaque type
|
= note: expected opaque type `impl !Sized`
found unit type `()`

error[E0271]: type mismatch resolving `() == impl !Sized`
--> $DIR/opaque-type-unsatisfied-bound.rs:22:16
|
LL | fn weird2() -> impl !Sized + Sized {}
| ^^^^^^^^^^^^^^^^^^^ types differ

error[E0277]: the trait bound `impl !Trait: Trait` is not satisfied
--> $DIR/opaque-type-unsatisfied-bound.rs:13:13
|
LL | consume(produce());
| ------- ^^^^^^^^^ the trait `Trait` is not implemented for `impl !Trait`
| |
| required by a bound introduced by this call
|
note: required by a bound in `consume`
--> $DIR/opaque-type-unsatisfied-bound.rs:10:20
|
LL | fn consume(_: impl Trait) {}
| ^^^^^ required by this bound in `consume`

error: aborting due to 7 previous errors; 1 warning emitted

Some errors have detailed explanations: E0271, E0277, E0308.
For more information about an error, try `rustc --explain E0271`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Znext-solver

#![feature(negative_bounds, unboxed_closures)]
//~^ WARN the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes

fn produce() -> impl !Fn<(u32,)> {}
//~^ ERROR mismatched types
//~| ERROR type mismatch resolving `() == impl !Fn<(u32,)>`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
warning: the feature `negative_bounds` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/opaque-type-unsatisfied-fn-bound.rs:3:12
|
LL | #![feature(negative_bounds, unboxed_closures)]
| ^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/opaque-type-unsatisfied-fn-bound.rs:6:34
|
LL | fn produce() -> impl !Fn<(u32,)> {}
| ---------------- ^^ types differ
| |
| the expected opaque type
|
= note: expected opaque type `impl !Fn<(u32,)>`
found unit type `()`

error[E0271]: type mismatch resolving `() == impl !Fn<(u32,)>`
--> $DIR/opaque-type-unsatisfied-fn-bound.rs:6:17
|
LL | fn produce() -> impl !Fn<(u32,)> {}
| ^^^^^^^^^^^^^^^^ types differ

error: aborting due to 2 previous errors; 1 warning emitted

Some errors have detailed explanations: E0271, E0308.
For more information about an error, try `rustc --explain E0271`.

0 comments on commit 255a0e9

Please sign in to comment.