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

Make "long type" printing type aware and trim types in E0275 #104469

Merged
merged 5 commits into from
Nov 19, 2022
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
37 changes: 37 additions & 0 deletions compiler/rustc_middle/src/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ use rustc_span::{BytePos, Span};
use rustc_target::spec::abi;

use std::borrow::Cow;
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;

use super::print::PrettyPrinter;

#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)]
pub struct ExpectedFound<T> {
Expand Down Expand Up @@ -985,6 +990,38 @@ fn foo(&self) -> Self::T { String::new() }
false
}

pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
let length_limit = 50;
let type_limit = 4;
let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
.pretty_print_type(ty)
.expect("could not write to `String`")
.into_buffer();
if regular.len() <= length_limit {
return (regular, None);
}
let short = FmtPrinter::new_with_limit(
self,
hir::def::Namespace::TypeNS,
rustc_session::Limit(type_limit),
)
.pretty_print_type(ty)
.expect("could not write to `String`")
.into_buffer();
if regular == short {
return (regular, None);
}
// Multiple types might be shortened in a single error, ensure we create a file for each.
let mut s = DefaultHasher::new();
ty.hash(&mut s);
let hash = s.finish();
let path = self.output_filenames(()).temp_path_ext(&format!("long-type-{hash}.txt"), None);
match std::fs::write(&path, &regular) {
Ok(_) => (short, Some(path)),
Err(_) => (regular, None),
}
}

fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String {
FmtPrinter::new(self, hir::def::Namespace::TypeNS)
.path_generic_args(Ok, args)
Expand Down
57 changes: 37 additions & 20 deletions compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,28 +276,45 @@ impl<'tcx> InstanceDef<'tcx> {
}
}

impl<'tcx> fmt::Display for Instance<'tcx> {
fn fmt_instance(
f: &mut fmt::Formatter<'_>,
instance: &Instance<'_>,
type_length: rustc_session::Limit,
) -> fmt::Result {
ty::tls::with(|tcx| {
let substs = tcx.lift(instance.substs).expect("could not lift for printing");

let s = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length)
.print_def_path(instance.def_id(), substs)?
.into_buffer();
f.write_str(&s)
})?;

match instance.def {
InstanceDef::Item(_) => Ok(()),
InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty),
InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
}
}

pub struct ShortInstance<'a, 'tcx>(pub &'a Instance<'tcx>, pub usize);

impl<'a, 'tcx> fmt::Display for ShortInstance<'a, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| {
let substs = tcx.lift(self.substs).expect("could not lift for printing");
let s = FmtPrinter::new(tcx, Namespace::ValueNS)
.print_def_path(self.def_id(), substs)?
.into_buffer();
f.write_str(&s)
})?;
fmt_instance(f, self.0, rustc_session::Limit(self.1))
}
}

match self.def {
InstanceDef::Item(_) => Ok(()),
InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty),
InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
}
impl<'tcx> fmt::Display for Instance<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| fmt_instance(f, self, tcx.type_length_limit()))
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub use self::context::{
GeneratorInteriorTypeCause, GlobalCtxt, Lift, OnDiskCache, TyCtxt, TypeckResults, UserType,
UserTypeAnnotationIndex,
};
pub use self::instance::{Instance, InstanceDef};
pub use self::instance::{Instance, InstanceDef, ShortInstance};
pub use self::list::List;
pub use self::parameterized::ParameterizedOverTcx;
pub use self::rvalue_scopes::RvalueScopes;
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, DefIdSet, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
use rustc_session::config::TrimmedDefPaths;
use rustc_session::cstore::{ExternCrate, ExternCrateSource};
use rustc_session::Limit;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;
Expand Down Expand Up @@ -1583,6 +1584,8 @@ pub struct FmtPrinterData<'a, 'tcx> {
region_index: usize,
binder_depth: usize,
printed_type_count: usize,
type_length_limit: Limit,
truncated: bool,

pub region_highlight_mode: RegionHighlightMode<'tcx>,

Expand All @@ -1605,6 +1608,10 @@ impl DerefMut for FmtPrinter<'_, '_> {

impl<'a, 'tcx> FmtPrinter<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, ns: Namespace) -> Self {
Self::new_with_limit(tcx, ns, tcx.type_length_limit())
}

pub fn new_with_limit(tcx: TyCtxt<'tcx>, ns: Namespace, type_length_limit: Limit) -> Self {
FmtPrinter(Box::new(FmtPrinterData {
tcx,
// Estimated reasonable capacity to allocate upfront based on a few
Expand All @@ -1617,6 +1624,8 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> {
region_index: 0,
binder_depth: 0,
printed_type_count: 0,
type_length_limit,
truncated: false,
region_highlight_mode: RegionHighlightMode::new(tcx),
ty_infer_name_resolver: None,
const_infer_name_resolver: None,
Expand Down Expand Up @@ -1751,11 +1760,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
}

fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
let type_length_limit = self.tcx.type_length_limit();
if type_length_limit.value_within_limit(self.printed_type_count) {
if self.type_length_limit.value_within_limit(self.printed_type_count) {
self.printed_type_count += 1;
self.pretty_print_type(ty)
} else {
self.truncated = true;
write!(self, "...")?;
Ok(self)
}
Expand Down
23 changes: 8 additions & 15 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
use rustc_session::Limit;
use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
use rustc_target::abi::Size;
use std::iter;
use std::ops::Range;
use std::path::PathBuf;

Expand Down Expand Up @@ -541,29 +540,23 @@ fn collect_items_rec<'tcx>(
}

/// Format instance name that is already known to be too long for rustc.
/// Show only the first and last 32 characters to avoid blasting
/// Show only the first 2 types if it is longer than 32 characters to avoid blasting
/// the user's terminal with thousands of lines of type-name.
///
/// If the type name is longer than before+after, it will be written to a file.
fn shrunk_instance_name<'tcx>(
tcx: TyCtxt<'tcx>,
instance: &Instance<'tcx>,
before: usize,
after: usize,
) -> (String, Option<PathBuf>) {
let s = instance.to_string();

// Only use the shrunk version if it's really shorter.
// This also avoids the case where before and after slices overlap.
if s.chars().nth(before + after + 1).is_some() {
// An iterator of all byte positions including the end of the string.
let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len()));

let shrunk = format!(
"{before}...{after}",
before = &s[..positions().nth(before).unwrap_or(s.len())],
after = &s[positions().rev().nth(after).unwrap_or(0)..],
);
if s.chars().nth(33).is_some() {
let shrunk = format!("{}", ty::ShortInstance(instance, 4));
if shrunk == s {
return (s, None);
}

let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None);
let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
Expand Down Expand Up @@ -599,7 +592,7 @@ fn check_recursion_limit<'tcx>(
if !recursion_limit.value_within_limit(adjusted_recursion_depth) {
let def_span = tcx.def_span(def_id);
let def_path_str = tcx.def_path_str(def_id);
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance);
let mut path = PathBuf::new();
let was_written = if written_to_path.is_some() {
path = written_to_path.unwrap();
Expand Down Expand Up @@ -641,7 +634,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
//
// Bail out in these cases to avoid that bad user experience.
if !tcx.type_length_limit().value_within_limit(type_length) {
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance);
let span = tcx.def_span(instance.def_id());
let mut path = PathBuf::new();
let was_written = if written_to_path.is_some() {
Expand Down
27 changes: 24 additions & 3 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use rustc_errors::{
MultiSpan, Style,
};
use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::GenericParam;
Expand All @@ -34,6 +35,7 @@ use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::print::{FmtPrinter, Print};
use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
TypeVisitable,
Expand Down Expand Up @@ -109,7 +111,10 @@ pub trait TypeErrCtxtExt<'tcx> {
suggest_increasing_limit: bool,
) -> !
where
T: fmt::Display + TypeFoldable<'tcx>;
T: fmt::Display
+ TypeFoldable<'tcx>
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;

fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);

Expand Down Expand Up @@ -468,15 +473,31 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
suggest_increasing_limit: bool,
) -> !
where
T: fmt::Display + TypeFoldable<'tcx>,
T: fmt::Display
+ TypeFoldable<'tcx>
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
{
let predicate = self.resolve_vars_if_possible(obligation.predicate.clone());
let mut pred_str = predicate.to_string();
if pred_str.len() > 50 {
// We don't need to save the type to a file, we will be talking about this type already
// in a separate note when we explain the obligation, so it will be available that way.
pred_str = predicate
.print(FmtPrinter::new_with_limit(
self.tcx,
Namespace::TypeNS,
rustc_session::Limit(6),
))
.unwrap()
.into_buffer();
}
let mut err = struct_span_err!(
self.tcx.sess,
obligation.cause.span,
E0275,
"overflow evaluating the requirement `{}`",
predicate
pred_str,
);

if suggest_increasing_limit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2733,9 +2733,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.resolve_vars_if_possible(data.derived.parent_trait_pred);
parent_trait_pred.remap_constness_diag(param_env);
let parent_def_id = parent_trait_pred.def_id();
let (self_ty, file) =
self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty());
let msg = format!(
"required for `{}` to implement `{}`",
parent_trait_pred.skip_binder().self_ty(),
"required for `{self_ty}` to implement `{}`",
parent_trait_pred.print_modifiers_and_trait_path()
);
let mut is_auto_trait = false;
Expand Down Expand Up @@ -2764,6 +2765,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
_ => err.note(&msg),
};

if let Some(file) = file {
err.note(&format!(
"the full type name has been written to '{}'",
file.display(),
));
}
let mut parent_predicate = parent_trait_pred;
let mut data = &data.derived;
let mut count = 0;
Expand Down Expand Up @@ -2804,11 +2811,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
count,
pluralize!(count)
));
let (self_ty, file) =
self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty());
err.note(&format!(
"required for `{}` to implement `{}`",
parent_trait_pred.skip_binder().self_ty(),
"required for `{self_ty}` to implement `{}`",
parent_trait_pred.print_modifiers_and_trait_path()
));
if let Some(file) = file {
err.note(&format!(
"the full type name has been written to '{}'",
file.display(),
));
}
}
// #74711: avoid a stack overflow
ensure_sufficient_stack(|| {
Expand Down
21 changes: 17 additions & 4 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::print::{FmtPrinter, Print};
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::SubstsRef;
use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
Expand Down Expand Up @@ -1081,11 +1082,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1);
}

fn check_recursion_depth<T: Display + TypeFoldable<'tcx>>(
fn check_recursion_depth<T>(
&self,
depth: usize,
error_obligation: &Obligation<'tcx, T>,
) -> Result<(), OverflowError> {
) -> Result<(), OverflowError>
where
T: fmt::Display
+ TypeFoldable<'tcx>
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
{
if !self.infcx.tcx.recursion_limit().value_within_limit(depth) {
match self.query_mode {
TraitQueryMode::Standard => {
Expand All @@ -1109,11 +1116,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// The weird return type of this function allows it to be used with the `try` (`?`)
/// operator within certain functions.
#[inline(always)]
fn check_recursion_limit<T: Display + TypeFoldable<'tcx>, V: Display + TypeFoldable<'tcx>>(
fn check_recursion_limit<T: Display + TypeFoldable<'tcx>, V>(
&self,
obligation: &Obligation<'tcx, T>,
error_obligation: &Obligation<'tcx, V>,
) -> Result<(), OverflowError> {
) -> Result<(), OverflowError>
where
V: fmt::Display
+ TypeFoldable<'tcx>
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
<V as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
{
self.check_recursion_depth(obligation.recursion_depth, error_obligation)
}

Expand Down
Loading