From 977546d3fc9f48702046214749521da4c459f614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Dec 2023 17:57:55 +0100 Subject: [PATCH] rustc_middle: Pretty-print negative bounds correctly --- compiler/rustc_middle/src/ty/print/pretty.rs | 72 +++++++++++++------ .../opaque-type-unsatisfied-bound.rs | 23 ++++++ .../opaque-type-unsatisfied-bound.stderr | 69 ++++++++++++++++++ .../opaque-type-unsatisfied-fn-bound.rs | 9 +++ .../opaque-type-unsatisfied-fn-bound.stderr | 21 ++++++ 5 files changed, 173 insertions(+), 21 deletions(-) create mode 100644 tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs create mode 100644 tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr create mode 100644 tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs create mode 100644 tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f7900d883ad37..6841685b0c457 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -912,7 +912,8 @@ 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 has_sized_bound = false; + let mut has_negative_sized_bound = false; let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new(); for (predicate, _) in bounds.iter_instantiated_copied(tcx, args) { @@ -922,13 +923,24 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { 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 => { + has_sized_bound = true; + continue; + } + ty::ImplPolarity::Negative => has_negative_sized_bound = true, + } } - 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); @@ -939,6 +951,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, @@ -955,7 +968,7 @@ 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 || !has_sized_bound; for (fn_once_trait_ref, entry) in fn_traits { write!(self, "{}", if first { "" } else { " + " })?; @@ -1002,18 +1015,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(); } } } @@ -1023,11 +1039,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); @@ -1094,9 +1114,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { })?; } - if !is_sized { - write!(self, "{}?Sized", if first { "" } else { " + " })?; - } else if first { + let add_sized = has_sized_bound && (first || has_negative_sized_bound); + let add_maybe_sized = !has_sized_bound && !has_negative_sized_bound; + if add_sized || add_maybe_sized { + if !first { + write!(self, " + ")?; + } + if add_maybe_sized { + write!(self, "?")?; + } write!(self, "Sized")?; } @@ -1128,9 +1154,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>>, >, fn_traits: &mut FxIndexMap, OpaqueFnEntry<'tcx>>, @@ -1139,7 +1166,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(); @@ -1167,7 +1197,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( diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs new file mode 100644 index 0000000000000..e1e93f7992065 --- /dev/null +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs @@ -0,0 +1,23 @@ +// compile-flags: -Znext-solver + +#![feature(negative_bounds, negative_impls)] + +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 + Sized` +fn weird1() -> impl !Sized + Sized {} +//~^ ERROR mismatched types +//~| ERROR type mismatch resolving `() == impl !Sized + Sized` +fn weird2() -> impl !Sized {} +//~^ ERROR mismatched types +//~| ERROR type mismatch resolving `() == impl !Sized` diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr new file mode 100644 index 0000000000000..627927618707f --- /dev/null +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr @@ -0,0 +1,69 @@ +error[E0308]: mismatched types + --> $DIR/opaque-type-unsatisfied-bound.rs:15:36 + | +LL | fn weird0() -> impl Sized + !Sized {} + | ------------------- ^^ types differ + | | + | the expected opaque type + | + = note: expected opaque type `impl !Sized + Sized` + found unit type `()` + +error[E0271]: type mismatch resolving `() == impl !Sized + Sized` + --> $DIR/opaque-type-unsatisfied-bound.rs:15:16 + | +LL | fn weird0() -> impl Sized + !Sized {} + | ^^^^^^^^^^^^^^^^^^^ types differ + +error[E0308]: mismatched types + --> $DIR/opaque-type-unsatisfied-bound.rs:18:36 + | +LL | fn weird1() -> impl !Sized + Sized {} + | ------------------- ^^ types differ + | | + | the expected opaque type + | + = note: expected opaque type `impl !Sized + Sized` + found unit type `()` + +error[E0271]: type mismatch resolving `() == impl !Sized + Sized` + --> $DIR/opaque-type-unsatisfied-bound.rs:18:16 + | +LL | fn weird1() -> impl !Sized + Sized {} + | ^^^^^^^^^^^^^^^^^^^ types differ + +error[E0308]: mismatched types + --> $DIR/opaque-type-unsatisfied-bound.rs:21:28 + | +LL | fn weird2() -> impl !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:21:16 + | +LL | fn weird2() -> impl !Sized {} + | ^^^^^^^^^^^ types differ + +error[E0277]: the trait bound `impl !Trait: Trait` is not satisfied + --> $DIR/opaque-type-unsatisfied-bound.rs:12: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:9:20 + | +LL | fn consume(_: impl Trait) {} + | ^^^^^ required by this bound in `consume` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0271, E0277, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs new file mode 100644 index 0000000000000..72bca1a8910b5 --- /dev/null +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs @@ -0,0 +1,9 @@ +// compile-flags: -Znext-solver + +#![feature(negative_bounds, unboxed_closures)] + +fn produce() -> impl !Fn<(u32,)> {} +//~^ ERROR mismatched types +//~| ERROR type mismatch resolving `() == impl !Fn<(u32,)>` + +fn main() {} diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr new file mode 100644 index 0000000000000..a4fb4b2b5c47f --- /dev/null +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/opaque-type-unsatisfied-fn-bound.rs:5: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:5:17 + | +LL | fn produce() -> impl !Fn<(u32,)> {} + | ^^^^^^^^^^^^^^^^ types differ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`.