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

Fix pretty printing of unsafe binders #137863

Merged
merged 2 commits into from
Mar 4, 2025
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
108 changes: 71 additions & 37 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ pub macro with_no_queries($e:expr) {{
))
}}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum WrapBinderMode {
ForAll,
Unsafe,
}
impl WrapBinderMode {
pub fn start_str(self) -> &'static str {
match self {
WrapBinderMode::ForAll => "for<",
WrapBinderMode::Unsafe => "unsafe<",
}
}
}

/// The "region highlights" are used to control region printing during
/// specific error messages. When a "region highlight" is enabled, it
/// gives an alternate way to print specific regions. For now, we
Expand Down Expand Up @@ -219,7 +233,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.print_def_path(def_id, args)
}

fn in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
fn print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
where
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
{
Expand All @@ -229,6 +243,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
fn wrap_binder<T, F: FnOnce(&T, &mut Self) -> Result<(), fmt::Error>>(
&mut self,
value: &ty::Binder<'tcx, T>,
_mode: WrapBinderMode,
f: F,
) -> Result<(), PrintError>
where
Expand Down Expand Up @@ -703,8 +718,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}
ty::FnPtr(ref sig_tys, hdr) => p!(print(sig_tys.with(hdr))),
ty::UnsafeBinder(ref bound_ty) => {
// FIXME(unsafe_binders): Make this print `unsafe<>` rather than `for<>`.
self.wrap_binder(bound_ty, |ty, cx| cx.pretty_print_type(*ty))?;
self.wrap_binder(bound_ty, WrapBinderMode::Unsafe, |ty, cx| {
cx.pretty_print_type(*ty)
})?;
}
ty::Infer(infer_ty) => {
if self.should_print_verbose() {
Expand Down Expand Up @@ -1067,29 +1083,33 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
};

if let Some(return_ty) = entry.return_ty {
self.wrap_binder(&bound_args_and_self_ty, |(args, _), cx| {
define_scoped_cx!(cx);
p!(write("{}", tcx.item_name(trait_def_id)));
p!("(");

for (idx, ty) in args.iter().enumerate() {
if idx > 0 {
p!(", ");
self.wrap_binder(
&bound_args_and_self_ty,
WrapBinderMode::ForAll,
|(args, _), cx| {
define_scoped_cx!(cx);
p!(write("{}", tcx.item_name(trait_def_id)));
p!("(");

for (idx, ty) in args.iter().enumerate() {
if idx > 0 {
p!(", ");
}
p!(print(ty));
}
p!(print(ty));
}

p!(")");
if let Some(ty) = return_ty.skip_binder().as_type() {
if !ty.is_unit() {
p!(" -> ", print(return_ty));
p!(")");
if let Some(ty) = return_ty.skip_binder().as_type() {
if !ty.is_unit() {
p!(" -> ", print(return_ty));
}
}
}
p!(write("{}", if paren_needed { ")" } else { "" }));
p!(write("{}", if paren_needed { ")" } else { "" }));

first = false;
Ok(())
})?;
first = false;
Ok(())
},
)?;
} else {
// Otherwise, render this like a regular trait.
traits.insert(
Expand All @@ -1110,7 +1130,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
for (trait_pred, assoc_items) in traits {
write!(self, "{}", if first { "" } else { " + " })?;

self.wrap_binder(&trait_pred, |trait_pred, cx| {
self.wrap_binder(&trait_pred, WrapBinderMode::ForAll, |trait_pred, cx| {
define_scoped_cx!(cx);

if trait_pred.polarity == ty::PredicatePolarity::Negative {
Expand Down Expand Up @@ -1302,7 +1322,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
let mut first = true;

if let Some(bound_principal) = predicates.principal() {
self.wrap_binder(&bound_principal, |principal, cx| {
self.wrap_binder(&bound_principal, WrapBinderMode::ForAll, |principal, cx| {
define_scoped_cx!(cx);
p!(print_def_path(principal.def_id, &[]));

Expand Down Expand Up @@ -1927,7 +1947,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn);

write!(self, "impl ")?;
self.wrap_binder(&sig, |sig, cx| {
self.wrap_binder(&sig, WrapBinderMode::ForAll, |sig, cx| {
define_scoped_cx!(cx);

p!(write("{kind}("));
Expand Down Expand Up @@ -2367,22 +2387,23 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
Ok(())
}

fn in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
fn print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
where
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
{
self.pretty_in_binder(value)
self.pretty_print_in_binder(value)
}

fn wrap_binder<T, C: FnOnce(&T, &mut Self) -> Result<(), PrintError>>(
&mut self,
value: &ty::Binder<'tcx, T>,
mode: WrapBinderMode,
f: C,
) -> Result<(), PrintError>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
self.pretty_wrap_binder(value, f)
self.pretty_wrap_binder(value, mode, f)
}

fn typed_value(
Expand Down Expand Up @@ -2632,6 +2653,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
pub fn name_all_regions<T>(
&mut self,
value: &ty::Binder<'tcx, T>,
mode: WrapBinderMode,
) -> Result<(T, UnordMap<ty::BoundRegion, ty::Region<'tcx>>), fmt::Error>
where
T: TypeFoldable<TyCtxt<'tcx>>,
Expand Down Expand Up @@ -2705,9 +2727,13 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
// anyways.
let (new_value, map) = if self.should_print_verbose() {
for var in value.bound_vars().iter() {
start_or_continue(self, "for<", ", ");
start_or_continue(self, mode.start_str(), ", ");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. This whole start or continue thing deserves a refactor. Your PR exposes how spaghetti this was. I think it's better to refactor after your PR lands tho, as then we have all the code there and can fix it together in one go

write!(self, "{var:?}")?;
}
// Unconditionally render `unsafe<>`.
if value.bound_vars().is_empty() && mode == WrapBinderMode::Unsafe {
start_or_continue(self, mode.start_str(), "");
}
start_or_continue(self, "", "> ");
(value.clone().skip_binder(), UnordMap::default())
} else {
Expand Down Expand Up @@ -2772,8 +2798,9 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
}
};

if !trim_path {
start_or_continue(self, "for<", ", ");
// Unconditionally render `unsafe<>`.
if !trim_path || mode == WrapBinderMode::Unsafe {
start_or_continue(self, mode.start_str(), ", ");
do_continue(self, name);
}
ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { var: br.var, kind })
Expand All @@ -2786,9 +2813,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
};
let new_value = value.clone().skip_binder().fold_with(&mut folder);
let region_map = folder.region_map;
if !trim_path {
start_or_continue(self, "", "> ");

if mode == WrapBinderMode::Unsafe && region_map.is_empty() {
start_or_continue(self, mode.start_str(), "");
}
start_or_continue(self, "", "> ");

(new_value, region_map)
};

Expand All @@ -2797,12 +2827,15 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
Ok((new_value, map))
}

pub fn pretty_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), fmt::Error>
pub fn pretty_print_in_binder<T>(
&mut self,
value: &ty::Binder<'tcx, T>,
) -> Result<(), fmt::Error>
where
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
{
let old_region_index = self.region_index;
let (new_value, _) = self.name_all_regions(value)?;
let (new_value, _) = self.name_all_regions(value, WrapBinderMode::ForAll)?;
new_value.print(self)?;
self.region_index = old_region_index;
self.binder_depth -= 1;
Expand All @@ -2812,13 +2845,14 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
pub fn pretty_wrap_binder<T, C: FnOnce(&T, &mut Self) -> Result<(), fmt::Error>>(
&mut self,
value: &ty::Binder<'tcx, T>,
mode: WrapBinderMode,
f: C,
) -> Result<(), fmt::Error>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let old_region_index = self.region_index;
let (new_value, _) = self.name_all_regions(value)?;
let (new_value, _) = self.name_all_regions(value, mode)?;
f(&new_value, self)?;
self.region_index = old_region_index;
self.binder_depth -= 1;
Expand Down Expand Up @@ -2877,7 +2911,7 @@ where
T: Print<'tcx, P> + TypeFoldable<TyCtxt<'tcx>>,
{
fn print(&self, cx: &mut P) -> Result<(), PrintError> {
cx.in_binder(self)
cx.print_in_binder(self)
}
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_symbol_mangling/src/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl<'tcx> SymbolMangler<'tcx> {
Ok(())
}

fn in_binder<T>(
fn wrap_binder<T>(
&mut self,
value: &ty::Binder<'tcx, T>,
print_value: impl FnOnce(&mut Self, &T) -> Result<(), PrintError>,
Expand Down Expand Up @@ -471,7 +471,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
ty::FnPtr(sig_tys, hdr) => {
let sig = sig_tys.with(hdr);
self.push("F");
self.in_binder(&sig, |cx, sig| {
self.wrap_binder(&sig, |cx, sig| {
if sig.safety.is_unsafe() {
cx.push("U");
}
Expand Down Expand Up @@ -554,7 +554,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
// [<Trait> [{<Projection>}]] [{<Auto>}]
// Since any predicates after the first one shouldn't change the binders,
// just put them all in the binders of the first.
self.in_binder(&predicates[0], |cx, _| {
self.wrap_binder(&predicates[0], |cx, _| {
for predicate in predicates.iter() {
// It would be nice to be able to validate bound vars here, but
// projections can actually include bound vars from super traits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ use rustc_middle::bug;
use rustc_middle::dep_graph::DepContext;
use rustc_middle::traits::PatternOriginExpr;
use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt};
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, with_forced_trimmed_paths};
use rustc_middle::ty::print::{
PrintError, PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths,
};
use rustc_middle::ty::{
self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt,
Expand Down Expand Up @@ -835,7 +837,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let get_lifetimes = |sig| {
use rustc_hir::def::Namespace;
let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
.name_all_regions(sig)
.name_all_regions(sig, WrapBinderMode::ForAll)
.unwrap();
let lts: Vec<String> =
reg.into_items().map(|(_, kind)| kind.to_string()).into_sorted_stable_ord();
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/unsafe-binders/type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![feature(unsafe_binders)]
//~^ WARN the feature `unsafe_binders` is incomplete

fn main() {
let x: unsafe<> i32 = 0;
//~^ ERROR mismatched types
let x: unsafe<'a> &'a i32 = &0;
//~^ ERROR mismatched types
}
34 changes: 34 additions & 0 deletions tests/ui/unsafe-binders/type-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/type-mismatch.rs:1:12
|
LL | #![feature(unsafe_binders)]
| ^^^^^^^^^^^^^^
|
= note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/type-mismatch.rs:5:27
|
LL | let x: unsafe<> i32 = 0;
| ------------ ^ expected `unsafe<> i32`, found integer
| |
| expected due to this
|
= note: expected unsafe binder `unsafe<> i32`
found type `{integer}`

error[E0308]: mismatched types
--> $DIR/type-mismatch.rs:7:33
|
LL | let x: unsafe<'a> &'a i32 = &0;
| ------------------ ^^ expected `unsafe<'a> &i32`, found `&{integer}`
| |
| expected due to this
|
= note: expected unsafe binder `unsafe<'a> &'a i32`
found reference `&{integer}`

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

For more information about this error, try `rustc --explain E0308`.
Loading