diff options
Diffstat (limited to 'compiler/rustc_middle/src')
38 files changed, 1290 insertions, 1236 deletions
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index f36ae831653..7520bc262c6 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use std::{fmt, io}; use rustc_errors::codes::*; @@ -6,7 +6,7 @@ use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; -use crate::ty::Ty; +use crate::ty::{Instance, Ty}; #[derive(Diagnostic)] #[diag(middle_drop_check_overflow, code = E0320)] @@ -161,13 +161,10 @@ pub(crate) struct ErroneousConstant { #[derive(Diagnostic)] #[diag(middle_type_length_limit)] #[help(middle_consider_type_length_limit)] -pub(crate) struct TypeLengthLimit { +pub(crate) struct TypeLengthLimit<'tcx> { #[primary_span] pub span: Span, - pub shrunk: String, - #[note(middle_written_to_path)] - pub was_written: bool, - pub path: PathBuf, + pub instance: Instance<'tcx>, pub type_length: usize, } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 803b645c8f7..cf0549fa668 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -57,8 +57,10 @@ #![feature(sized_hierarchy)] #![feature(try_blocks)] #![feature(try_trait_v2)] +#![feature(try_trait_v2_residual)] #![feature(try_trait_v2_yeet)] #![feature(type_alias_impl_trait)] +#![feature(unwrap_infallible)] #![feature(yeet_expr)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 341a735f88f..f70b7b6fad7 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -211,11 +211,28 @@ impl LintExpectation { } fn explain_lint_level_source( + sess: &Session, lint: &'static Lint, level: Level, src: LintLevelSource, err: &mut Diag<'_, ()>, ) { + // Find the name of the lint group that contains the given lint. + // Assumes the lint only belongs to one group. + let lint_group_name = |lint| { + let lint_groups_iter = sess.lint_groups_iter(); + let lint_id = LintId::of(lint); + lint_groups_iter + .filter(|lint_group| !lint_group.is_externally_loaded) + .find(|lint_group| { + lint_group + .lints + .iter() + .find(|lint_group_lint| **lint_group_lint == lint_id) + .is_some() + }) + .map(|lint_group| lint_group.name) + }; let name = lint.name_lower(); if let Level::Allow = level { // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint @@ -224,7 +241,15 @@ fn explain_lint_level_source( } match src { LintLevelSource::Default => { - err.note_once(format!("`#[{}({})]` on by default", level.as_str(), name)); + let level_str = level.as_str(); + match lint_group_name(lint) { + Some(group_name) => { + err.note_once(format!("`#[{level_str}({name})]` (part of `#[{level_str}({group_name})]`) on by default")); + } + None => { + err.note_once(format!("`#[{level_str}({name})]` on by default")); + } + } } LintLevelSource::CommandLine(lint_flag_val, orig_level) => { let flag = orig_level.to_cmd_flag(); @@ -427,7 +452,7 @@ pub fn lint_level( decorate(&mut err); } - explain_lint_level_source(lint, level, src, &mut err); + explain_lint_level_source(sess, lint, level, src, &mut err); err.emit() } lint_level_impl(sess, lint, level, span, Box::new(decorate)) diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 94384e64afd..78cafe8566b 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,13 +1,12 @@ use std::borrow::Cow; use rustc_abi::Align; -use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; -use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr}; +use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; -use crate::mir::mono::Linkage; use crate::ty::{InstanceKind, TyCtxt}; impl<'tcx> TyCtxt<'tcx> { @@ -36,14 +35,10 @@ pub struct CodegenFnAttrs { pub inline: InlineAttr, /// Parsed representation of the `#[optimize]` attribute pub optimize: OptimizeAttr, - /// The `#[export_name = "..."]` attribute, indicating a custom symbol a - /// function should be exported under - pub export_name: Option<Symbol>, - /// The `#[link_name = "..."]` attribute, indicating a custom symbol an - /// imported function should be imported as. Note that `export_name` - /// probably isn't set when this is set, this is for foreign items while - /// `#[export_name]` is for Rust-defined functions. - pub link_name: Option<Symbol>, + /// The name this function will be imported/exported under. This can be set + /// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute + /// depending on if this is a function definition or foreign function. + pub symbol_name: Option<Symbol>, /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an /// imported function has in the dynamic library. Note that this must not /// be set when `link_name` is set. This is for foreign items with the @@ -62,8 +57,8 @@ pub struct CodegenFnAttrs { /// The `#[link_section = "..."]` attribute, or what executable section this /// should be placed in. pub link_section: Option<Symbol>, - /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which - /// instrumentation should be disabled inside the annotated function. + /// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which + /// instrumentation should be disabled inside the function. pub no_sanitize: SanitizerSet, /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should /// be generated against a specific instruction set. Only usable on architectures which allow @@ -75,17 +70,25 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option<PatchableFunctionEntry>, - /// For the `#[autodiff]` macros. - pub autodiff_item: Option<AutoDiffAttrs>, +} + +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)] +pub enum TargetFeatureKind { + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute + Implied, + /// The feature is added by the regular `target_feature` attribute. + Enabled, + /// The feature is added by the unsafe `force_target_feature` attribute. + Forced, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct TargetFeature { /// The name of the target feature (e.g. "avx") pub name: Symbol, - /// The feature is implied by another feature, rather than explicitly added by the - /// `#[target_feature]` attribute - pub implied: bool, + /// The way this feature was enabled. + pub kind: TargetFeatureKind, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -170,8 +173,7 @@ impl CodegenFnAttrs { flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, optimize: OptimizeAttr::Default, - export_name: None, - link_name: None, + symbol_name: None, link_ordinal: None, target_features: vec![], safe_target_features: false, @@ -182,7 +184,6 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, - autodiff_item: None, } } @@ -193,10 +194,14 @@ impl CodegenFnAttrs { /// * `#[linkage]` is present /// /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint. - pub fn contains_extern_indicator(&self) -> bool { + pub fn contains_extern_indicator(&self, tcx: TyCtxt<'_>, did: DefId) -> bool { + if tcx.is_foreign_item(did) { + return false; + } + self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - || self.export_name.is_some() + || self.symbol_name.is_some() || match self.linkage { // These are private, so make sure we don't try to consider // them external. diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 785ddd1ee29..e3e04c9d180 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -181,11 +181,7 @@ impl EffectiveVisibilities { // nominal visibility. For some items nominal visibility doesn't make sense so we // don't check this condition for them. let is_impl = matches!(tcx.def_kind(def_id), DefKind::Impl { .. }); - let is_associated_item_in_trait_impl = tcx - .impl_of_assoc(def_id.to_def_id()) - .and_then(|impl_id| tcx.trait_id_of_impl(impl_id)) - .is_some(); - if !is_impl && !is_associated_item_in_trait_impl { + if !is_impl && tcx.trait_impl_of_assoc(def_id.to_def_id()).is_none() { let nominal_vis = tcx.visibility(def_id); if !nominal_vis.is_at_least(ev.reachable, tcx) { span_bug!( diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 800c1af660a..857d041224f 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -96,6 +96,7 @@ impl fmt::Debug for Scope { ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.local_id), ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id), ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id), + ScopeData::MatchGuard => write!(fmt, "MatchGuard({:?})", self.local_id), ScopeData::Remainder(fsi) => write!( fmt, "Remainder {{ block: {:?}, first_statement_index: {}}}", @@ -131,6 +132,11 @@ pub enum ScopeData { /// whose lifetimes do not cross beyond this scope. IfThenRescope, + /// Scope of the condition and body of a match arm with a guard + /// Used for variables introduced in an if-let guard, + /// whose lifetimes do not cross beyond this scope. + MatchGuard, + /// Scope following a `let id = expr;` binding in a block. Remainder(FirstStatementIndex), } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 4a19cf1563c..18520089e3e 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_errors::{Applicability, Diag, EmissionGuarantee}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer}; use rustc_feature::GateIssue; use rustc_hir::attrs::{DeprecatedSince, Deprecation}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -12,7 +12,7 @@ use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stabil use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_session::Session; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; +use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint}; use rustc_session::parse::feature_err_issue; use rustc_span::{Span, Symbol, sym}; use tracing::debug; diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 96131d47a17..164433aede2 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -448,6 +448,11 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } + #[inline] + pub fn from_ty_value(tcx: TyCtxt<'tcx>, val: ty::Value<'tcx>) -> Self { + Self::Ty(val.ty, ty::Const::new_value(tcx, val.valtree, val.ty)) + } + pub fn from_bits( tcx: TyCtxt<'tcx>, bits: u128, diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index e26575b552e..fd4c64b9a61 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -50,25 +50,6 @@ rustc_index::newtype_index! { pub struct ExpressionId {} } -rustc_index::newtype_index! { - /// ID of a mcdc condition. Used by llvm to check mcdc coverage. - /// - /// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not - /// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19) - /// and represents it with `int16_t`. This max value may be changed once we could - /// figure out an accurate limit. - #[derive(HashStable)] - #[encodable] - #[orderable] - #[max = 0xFFFF] - #[debug_format = "ConditionId({})"] - pub struct ConditionId {} -} - -impl ConditionId { - pub const START: Self = Self::from_usize(0); -} - /// Enum that can hold a constant zero value, the ID of an physical coverage /// counter, or the ID of a coverage-counter expression. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -109,16 +90,6 @@ pub enum CoverageKind { /// During codegen, this might be lowered to `llvm.instrprof.increment` or /// to a no-op, depending on the outcome of counter-creation. VirtualCounter { bcb: BasicCoverageBlock }, - - /// Marks the point in MIR control flow represented by a evaluated condition. - /// - /// This is eventually lowered to instruments updating mcdc temp variables. - CondBitmapUpdate { index: u32, decision_depth: u16 }, - - /// Marks the point in MIR control flow represented by a evaluated decision. - /// - /// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR. - TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 }, } impl Debug for CoverageKind { @@ -128,12 +99,6 @@ impl Debug for CoverageKind { SpanMarker => write!(fmt, "SpanMarker"), BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"), - CondBitmapUpdate { index, decision_depth } => { - write!(fmt, "CondBitmapUpdate(index={:?}, depth={:?})", index, decision_depth) - } - TestVectorBitmapUpdate { bitmap_idx, decision_depth } => { - write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth) - } } } } @@ -170,14 +135,6 @@ pub enum MappingKind { Code { bcb: BasicCoverageBlock }, /// Associates a branch region with separate counters for true and false. Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, - /// Associates a branch region with separate counters for true and false. - MCDCBranch { - true_bcb: BasicCoverageBlock, - false_bcb: BasicCoverageBlock, - mcdc_params: ConditionInfo, - }, - /// Associates a decision region with a bitmap and number of conditions. - MCDCDecision(DecisionInfo), } #[derive(Clone, Debug)] @@ -201,11 +158,6 @@ pub struct FunctionCoverageInfo { pub priority_list: Vec<BasicCoverageBlock>, pub mappings: Vec<Mapping>, - - pub mcdc_bitmap_bits: usize, - /// The depth of the deepest decision is used to know how many - /// temp condbitmaps should be allocated for the function. - pub mcdc_num_condition_bitmaps: usize, } /// Coverage information for a function, recorded during MIR building and @@ -222,10 +174,6 @@ pub struct CoverageInfoHi { /// data structures without having to scan the entire body first. pub num_block_markers: usize, pub branch_spans: Vec<BranchSpan>, - /// Branch spans generated by mcdc. Because of some limits mcdc builder give up generating - /// decisions including them so that they are handled as normal branch spans. - pub mcdc_degraded_branch_spans: Vec<MCDCBranchSpan>, - pub mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>, } #[derive(Clone, Debug)] @@ -236,39 +184,6 @@ pub struct BranchSpan { pub false_marker: BlockMarkerId, } -#[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable)] -pub struct ConditionInfo { - pub condition_id: ConditionId, - pub true_next_id: Option<ConditionId>, - pub false_next_id: Option<ConditionId>, -} - -#[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable)] -pub struct MCDCBranchSpan { - pub span: Span, - pub condition_info: ConditionInfo, - pub true_marker: BlockMarkerId, - pub false_marker: BlockMarkerId, -} - -#[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable)] -pub struct DecisionInfo { - pub bitmap_idx: u32, - pub num_conditions: u16, -} - -#[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable)] -pub struct MCDCDecisionSpan { - pub span: Span, - pub end_markers: Vec<BlockMarkerId>, - pub decision_depth: u16, - pub num_conditions: usize, -} - /// Contains information needed during codegen, obtained by inspecting the /// function's MIR after MIR optimizations. /// diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 27ead514531..2ea92a39d48 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -306,8 +306,6 @@ pub enum AllocError { ScalarSizeMismatch(ScalarSizeMismatch), /// Encountered a pointer where we needed raw bytes. ReadPointerAsInt(Option<BadBytesAccess>), - /// Partially overwriting a pointer. - OverwritePartialPointer(Size), /// Partially copying a pointer. ReadPartialPointer(Size), /// Using uninitialized data where it is not allowed. @@ -331,9 +329,6 @@ impl AllocError { ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( - UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), - ), ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), @@ -633,11 +628,11 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<&mut [u8]> { + ) -> &mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); - Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) + &mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()] } /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases @@ -646,15 +641,15 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<*mut [u8]> { + ) -> *mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check // Crucially, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); - Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) + ptr::slice_from_raw_parts_mut(begin_ptr, len) } /// This gives direct mutable access to the entire buffer, just exposing their internal state @@ -723,26 +718,45 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> let ptr = Pointer::new(prov, Size::from_bytes(bits)); return Ok(Scalar::from_pointer(ptr, cx)); } - - // If we can work on pointers byte-wise, join the byte-wise provenances. - if Prov::OFFSET_IS_ADDR { - let mut prov = self.provenance.get(range.start, cx); + // The other easy case is total absence of provenance. + if self.provenance.range_empty(range, cx) { + return Ok(Scalar::from_uint(bits, range.size)); + } + // If we get here, we have to check per-byte provenance, and join them together. + let prov = 'prov: { + // Initialize with first fragment. Must have index 0. + let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { + break 'prov None; + }; + // Update with the remaining fragments. for offset in Size::from_bytes(1)..range.size { - let this_prov = self.provenance.get(range.start + offset, cx); - prov = Prov::join(prov, this_prov); + // Ensure there is provenance here and it has the right index. + let Some((frag_prov, frag_idx)) = + self.provenance.get_byte(range.start + offset, cx) + else { + break 'prov None; + }; + // Wildcard provenance is allowed to come with any index (this is needed + // for Miri's native-lib mode to work). + if u64::from(frag_idx) != offset.bytes() && Some(frag_prov) != Prov::WILDCARD { + break 'prov None; + } + // Merge this byte's provenance with the previous ones. + joint_prov = match Prov::join(joint_prov, frag_prov) { + Some(prov) => prov, + None => break 'prov None, + }; } - // Now use this provenance. - let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(Scalar::from_maybe_pointer(ptr, cx)); - } else { - // Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of - // provenance. - if self.provenance.range_empty(range, cx) { - return Ok(Scalar::from_uint(bits, range.size)); - } - // Else we have mixed provenance, that doesn't work. + break 'prov Some(joint_prov); + }; + if prov.is_none() && !Prov::OFFSET_IS_ADDR { + // There are some bytes with provenance here but overall the provenance does not add up. + // We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we must error. return Err(AllocError::ReadPartialPointer(range.start)); } + // We can use this provenance. + let ptr = Pointer::new(prov, Size::from_bytes(bits)); + return Ok(Scalar::from_maybe_pointer(ptr, cx)); } else { // We are *not* reading a pointer. // If we can just ignore provenance or there is none, that's easy. @@ -782,7 +796,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> let endian = cx.data_layout().endian; // Yes we do overwrite all the bytes in `dst`. - let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; + let dst = self.get_bytes_unchecked_for_overwrite(cx, range); write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. @@ -795,10 +809,9 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> } /// Write "uninit" to the given memory range. - pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { + pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) { self.mark_init(range, false); - self.provenance.clear(range, cx)?; - Ok(()) + self.provenance.clear(range, cx); } /// Mark all bytes in the given range as initialised and reset the provenance @@ -817,9 +830,12 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> } /// Remove all provenance in the given memory range. - pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { - self.provenance.clear(range, cx)?; - return Ok(()); + pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) { + self.provenance.clear(range, cx); + } + + pub fn provenance_merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + self.provenance.merge_bytes(cx) } /// Applies a previously prepared provenance copy. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 119d4be64e6..dbbd95408c8 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -10,7 +10,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::trace; -use super::{AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance, alloc_range}; +use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -19,25 +19,25 @@ pub struct ProvenanceMap<Prov = CtfeProvenance> { /// `Provenance` in this map applies from the given offset for an entire pointer-size worth of /// bytes. Two entries in this map are always at least a pointer size apart. ptrs: SortedMap<Size, Prov>, - /// Provenance in this map only applies to the given single byte. - /// This map is disjoint from the previous. It will always be empty when - /// `Prov::OFFSET_IS_ADDR` is false. - bytes: Option<Box<SortedMap<Size, Prov>>>, + /// This stores byte-sized provenance fragments. + /// The `u8` indicates the position of this byte inside its original pointer. + /// If the bytes are re-assembled in their original order, the pointer can be used again. + /// Wildcard provenance is allowed to have index 0 everywhere. + bytes: Option<Box<SortedMap<Size, (Prov, u8)>>>, } // These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable // for some particular `D`/`S`. impl<D: Decoder, Prov: Provenance + Decodable<D>> Decodable<D> for ProvenanceMap<Prov> { fn decode(d: &mut D) -> Self { - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized + // `bytes` is not in the serialized format Self { ptrs: Decodable::decode(d), bytes: None } } } impl<S: Encoder, Prov: Provenance + Encodable<S>> Encodable<S> for ProvenanceMap<Prov> { fn encode(&self, s: &mut S) { let Self { ptrs, bytes } = self; - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized - debug_assert!(bytes.is_none()); // without `OFFSET_IS_ADDR`, this is always empty + assert!(bytes.is_none()); // interning refuses allocations with pointer fragments ptrs.encode(s) } } @@ -58,10 +58,10 @@ impl ProvenanceMap { /// Give access to the ptr-sized provenances (which can also be thought of as relocations, and /// indeed that is how codegen treats them). /// - /// Only exposed with `CtfeProvenance` provenance, since it panics if there is bytewise provenance. + /// Only use on interned allocations, as other allocations may have per-byte provenance! #[inline] pub fn ptrs(&self) -> &SortedMap<Size, CtfeProvenance> { - debug_assert!(self.bytes.is_none()); // `CtfeProvenance::OFFSET_IS_ADDR` is false so this cannot fail + assert!(self.bytes.is_none(), "`ptrs()` called on non-interned allocation"); &self.ptrs } } @@ -88,12 +88,12 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { } /// `pm.range_ptrs_is_empty(r, cx)` == `pm.range_ptrs_get(r, cx).is_empty()`, but is faster. - pub(super) fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { + fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx)) } /// Returns all byte-wise provenance in the given range. - fn range_bytes_get(&self, range: AllocRange) -> &[(Size, Prov)] { + fn range_bytes_get(&self, range: AllocRange) -> &[(Size, (Prov, u8))] { if let Some(bytes) = self.bytes.as_ref() { bytes.range(range.start..range.end()) } else { @@ -107,19 +107,58 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { } /// Get the provenance of a single byte. - pub fn get(&self, offset: Size, cx: &impl HasDataLayout) -> Option<Prov> { + pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u8)> { let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx); debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); - Some(entry.1) + Some((entry.1, (offset - entry.0).bytes() as u8)) } else { // Look up per-byte provenance. self.bytes.as_ref().and_then(|b| b.get(&offset).copied()) } } + /// Gets the provenances of all bytes (including from pointers) in a range. + pub fn get_range( + &self, + cx: &impl HasDataLayout, + range: AllocRange, + ) -> impl Iterator<Item = Prov> { + let ptr_provs = self.range_ptrs_get(range, cx).iter().map(|(_, p)| *p); + let byte_provs = self.range_bytes_get(range).iter().map(|(_, (p, _))| *p); + ptr_provs.chain(byte_provs) + } + + /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments + /// sit next to each other. Return `false` is that is not possible due to partial pointers. + pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + let Some(bytes) = self.bytes.as_deref_mut() else { + return true; + }; + let ptr_size = cx.data_layout().pointer_size(); + while let Some((offset, (prov, _))) = bytes.iter().next().copied() { + // Check if this fragment starts a pointer. + let range = offset..offset + ptr_size; + let frags = bytes.range(range.clone()); + if frags.len() != ptr_size.bytes_usize() { + return false; + } + for (idx, (_offset, (frag_prov, frag_idx))) in frags.iter().copied().enumerate() { + if frag_prov != prov || frag_idx != idx as u8 { + return false; + } + } + // Looks like a pointer! Move it over to the ptr provenance map. + bytes.remove_range(range); + self.ptrs.insert(offset, prov); + } + // We managed to convert everything into whole pointers. + self.bytes = None; + true + } + /// Check if there is ptr-sized provenance at the given index. /// Does not mean anything for bytewise provenance! But can be useful as an optimization. pub fn get_ptr(&self, offset: Size) -> Option<Prov> { @@ -137,7 +176,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Yields all the provenances stored in this map. pub fn provenances(&self) -> impl Iterator<Item = Prov> { - let bytes = self.bytes.iter().flat_map(|b| b.values()); + let bytes = self.bytes.iter().flat_map(|b| b.values().map(|(p, _i)| p)); self.ptrs.values().chain(bytes).copied() } @@ -148,16 +187,12 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Removes all provenance inside the given range. /// If there is provenance overlapping with the edges, might result in an error. - pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) -> AllocResult { + pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { let start = range.start; let end = range.end(); // Clear the bytewise part -- this is easy. - if Prov::OFFSET_IS_ADDR { - if let Some(bytes) = self.bytes.as_mut() { - bytes.remove_range(start..end); - } - } else { - debug_assert!(self.bytes.is_none()); + if let Some(bytes) = self.bytes.as_mut() { + bytes.remove_range(start..end); } let pointer_size = cx.data_layout().pointer_size(); @@ -168,7 +203,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // Find all provenance overlapping the given range. if self.range_ptrs_is_empty(range, cx) { // No provenance in this range, we are done. This is the common case. - return Ok(()); + return; } // This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much @@ -179,28 +214,20 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // We need to handle clearing the provenance from parts of a pointer. if first < start { - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(first)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&first]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in first..start { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - first).bytes() as u8)); } } if last > end { let begin_of_last = last - pointer_size; - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(begin_of_last)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&begin_of_last]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in end..last { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - begin_of_last).bytes() as u8)); } } @@ -208,8 +235,6 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine, // i.e., this will not remove any other provenance just after the ones we care about. self.ptrs.remove_range(first..last); - - Ok(()) } /// Overwrites all provenance in the given range with wildcard provenance. @@ -218,10 +243,6 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// /// Provided for usage in Miri and panics otherwise. pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) { - assert!( - Prov::OFFSET_IS_ADDR, - "writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false" - ); let wildcard = Prov::WILDCARD.unwrap(); let bytes = self.bytes.get_or_insert_with(Box::default); @@ -229,21 +250,22 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // Remove pointer provenances that overlap with the range, then readd the edge ones bytewise. let ptr_range = Self::adjusted_range_ptrs(range, cx); let ptrs = self.ptrs.range(ptr_range.clone()); - if let Some((offset, prov)) = ptrs.first() { - for byte_ofs in *offset..range.start { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.first().copied() { + for byte_ofs in offset..range.start { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } - if let Some((offset, prov)) = ptrs.last() { - for byte_ofs in range.end()..*offset + cx.data_layout().pointer_size() { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.last().copied() { + for byte_ofs in range.end()..offset + cx.data_layout().pointer_size() { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } self.ptrs.remove_range(ptr_range); // Overwrite bytewise provenance. for offset in range.start..range.end() { - bytes.insert(offset, wildcard); + // The fragment index does not matter for wildcard provenance. + bytes.insert(offset, (wildcard, 0)); } } } @@ -253,7 +275,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Offsets are already adjusted to the destination allocation. pub struct ProvenanceCopy<Prov> { dest_ptrs: Option<Box<[(Size, Prov)]>>, - dest_bytes: Option<Box<[(Size, Prov)]>>, + dest_bytes: Option<Box<[(Size, (Prov, u8))]>>, } impl<Prov: Provenance> ProvenanceMap<Prov> { @@ -263,7 +285,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { dest: Size, count: u64, cx: &impl HasDataLayout, - ) -> AllocResult<ProvenanceCopy<Prov>> { + ) -> ProvenanceCopy<Prov> { let shift_offset = move |idx, offset| { // compute offset for current repetition let dest_offset = dest + src.size * idx; // `Size` operations @@ -301,24 +323,16 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { let mut dest_bytes_box = None; let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); - if !Prov::OFFSET_IS_ADDR { - // There can't be any bytewise provenance, and we cannot split up the begin/end overlap. - if let Some(entry) = begin_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - if let Some(entry) = end_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - debug_assert!(self.bytes.is_none()); - } else { - let mut bytes = Vec::new(); + // We only need to go here if there is some overlap or some bytewise provenance. + if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { + let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); // For really small copies, make sure we don't run off the end of the `src` range. let entry_end = cmp::min(entry.0 + ptr_size, src.end()); for offset in src.start..entry_end { - bytes.push((offset, entry.1)); + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); @@ -334,8 +348,9 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { let entry_start = cmp::max(entry.0, src.start); for offset in entry_start..src.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { - // The last entry, if it exists, has a lower offset than us. - bytes.push((offset, entry.1)); + // The last entry, if it exists, has a lower offset than us, so we + // can add it at the end and remain sorted. + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we @@ -358,7 +373,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { dest_bytes_box = Some(dest_bytes.into_boxed_slice()); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box } } /// Applies a provenance copy. @@ -368,14 +383,10 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { if let Some(dest_ptrs) = copy.dest_ptrs { self.ptrs.insert_presorted(dest_ptrs.into()); } - if Prov::OFFSET_IS_ADDR { - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); - } - } else { - debug_assert!(copy.dest_bytes.is_none()); + if let Some(dest_bytes) = copy.dest_bytes + && !dest_bytes.is_empty() + { + self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 3e895c6b280..7c72a8ec243 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -582,9 +582,6 @@ pub enum UnsupportedOpInfo { // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // - /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state - /// cannot be represented by the CTFE interpreter. - OverwritePartialPointer(Pointer<AllocId>), /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute /// addresses, the resulting state cannot be represented by the CTFE interpreter. ReadPartialPointer(Pointer<AllocId>), @@ -793,36 +790,37 @@ impl Drop for Guard { /// We also make things panic if this type is ever implicitly dropped. #[derive(Debug)] #[must_use] -pub struct InterpResult_<'tcx, T> { +pub struct InterpResult<'tcx, T = ()> { res: Result<T, InterpErrorInfo<'tcx>>, guard: Guard, } -// Type alias to be able to set a default type argument. -pub type InterpResult<'tcx, T = ()> = InterpResult_<'tcx, T>; - -impl<'tcx, T> ops::Try for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::Try for InterpResult<'tcx, T> { type Output = T; - type Residual = InterpResult_<'tcx, convert::Infallible>; + type Residual = InterpResult<'tcx, convert::Infallible>; #[inline] fn from_output(output: Self::Output) -> Self { - InterpResult_::new(Ok(output)) + InterpResult::new(Ok(output)) } #[inline] fn branch(self) -> ops::ControlFlow<Self::Residual, Self::Output> { match self.disarm() { Ok(v) => ops::ControlFlow::Continue(v), - Err(e) => ops::ControlFlow::Break(InterpResult_::new(Err(e))), + Err(e) => ops::ControlFlow::Break(InterpResult::new(Err(e))), } } } -impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::Residual<T> for InterpResult<'tcx, convert::Infallible> { + type TryType = InterpResult<'tcx, T>; +} + +impl<'tcx, T> ops::FromResidual for InterpResult<'tcx, T> { #[inline] #[track_caller] - fn from_residual(residual: InterpResult_<'tcx, convert::Infallible>) -> Self { + fn from_residual(residual: InterpResult<'tcx, convert::Infallible>) -> Self { match residual.disarm() { Err(e) => Self::new(Err(e)), } @@ -830,7 +828,7 @@ impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { } // Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. -impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult<'tcx, T> { #[inline] fn from_residual(ops::Yeet(e): ops::Yeet<InterpErrorKind<'tcx>>) -> Self { Self::new(Err(e.into())) @@ -840,7 +838,7 @@ impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResu // Allow `?` on `Result<_, InterpError>` in functions returning `InterpResult_`. // This is useful e.g. for `option.ok_or_else(|| err_ub!(...))`. impl<'tcx, T, E: Into<InterpErrorInfo<'tcx>>> ops::FromResidual<Result<convert::Infallible, E>> - for InterpResult_<'tcx, T> + for InterpResult<'tcx, T> { #[inline] fn from_residual(residual: Result<convert::Infallible, E>) -> Self { @@ -863,7 +861,7 @@ impl<'tcx, T, V: FromIterator<T>> FromIterator<InterpResult<'tcx, T>> for Interp } } -impl<'tcx, T> InterpResult_<'tcx, T> { +impl<'tcx, T> InterpResult<'tcx, T> { #[inline(always)] fn new(res: Result<T, InterpErrorInfo<'tcx>>) -> Self { Self { res, guard: Guard } @@ -890,7 +888,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { #[inline] pub fn map<U>(self, f: impl FnOnce(T) -> U) -> InterpResult<'tcx, U> { - InterpResult_::new(self.disarm().map(f)) + InterpResult::new(self.disarm().map(f)) } #[inline] @@ -898,7 +896,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, ) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().map_err(f)) + InterpResult::new(self.disarm().map_err(f)) } #[inline] @@ -906,7 +904,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { self, f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>, ) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().map_err(|mut e| { + InterpResult::new(self.disarm().map_err(|mut e| { e.0.kind = f(e.0.kind); e })) @@ -914,7 +912,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { #[inline] pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind))) + InterpResult::new(self.disarm().inspect_err(|e| f(&e.0.kind))) } #[inline] @@ -937,7 +935,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { #[inline] pub fn and_then<U>(self, f: impl FnOnce(T) -> InterpResult<'tcx, U>) -> InterpResult<'tcx, U> { - InterpResult_::new(self.disarm().and_then(|t| f(t).disarm())) + InterpResult::new(self.disarm().and_then(|t| f(t).disarm())) } /// Returns success if both `self` and `other` succeed, while ensuring we don't @@ -952,7 +950,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { // Discard the other error. drop(other.disarm()); // Return `self`. - InterpResult_::new(Err(e)) + InterpResult::new(Err(e)) } } } @@ -960,5 +958,5 @@ impl<'tcx, T> InterpResult_<'tcx, T> { #[inline(always)] pub fn interp_ok<'tcx, T>(x: T) -> InterpResult<'tcx, T> { - InterpResult_::new(Ok(x)) + InterpResult::new(Ok(x)) } diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index e25ff7651f6..c581b2ebf09 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -56,7 +56,7 @@ impl<T: HasDataLayout> PointerArithmetic for T {} /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to /// some global state. /// The `Debug` rendering is used to display bare provenance, and for the default impl of `fmt`. -pub trait Provenance: Copy + fmt::Debug + 'static { +pub trait Provenance: Copy + PartialEq + fmt::Debug + 'static { /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are /// different from what the Abstract Machine prescribes, so the interpreter must prevent any @@ -79,7 +79,7 @@ pub trait Provenance: Copy + fmt::Debug + 'static { fn get_alloc_id(self) -> Option<AllocId>; /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance. - fn join(left: Option<Self>, right: Option<Self>) -> Option<Self>; + fn join(left: Self, right: Self) -> Option<Self>; } /// The type of provenance in the compile-time interpreter. @@ -192,8 +192,8 @@ impl Provenance for CtfeProvenance { Some(self.alloc_id()) } - fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(left: Self, right: Self) -> Option<Self> { + if left == right { Some(left) } else { None } } } @@ -224,8 +224,8 @@ impl Provenance for AllocId { Some(self) } - fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(_left: Self, _right: Self) -> Option<Self> { + unreachable!() } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c55c7fc6002..c977e5329c2 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -115,48 +115,6 @@ impl MirPhase { MirPhase::Runtime(runtime_phase) => (3, 1 + runtime_phase as usize), } } - - /// Parses a `MirPhase` from a pair of strings. Panics if this isn't possible for any reason. - pub fn parse(dialect: String, phase: Option<String>) -> Self { - match &*dialect.to_ascii_lowercase() { - "built" => { - assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); - MirPhase::Built - } - "analysis" => Self::Analysis(AnalysisPhase::parse(phase)), - "runtime" => Self::Runtime(RuntimePhase::parse(phase)), - _ => bug!("Unknown MIR dialect: '{}'", dialect), - } - } -} - -impl AnalysisPhase { - pub fn parse(phase: Option<String>) -> Self { - let Some(phase) = phase else { - return Self::Initial; - }; - - match &*phase.to_ascii_lowercase() { - "initial" => Self::Initial, - "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, - _ => bug!("Unknown analysis phase: '{}'", phase), - } - } -} - -impl RuntimePhase { - pub fn parse(phase: Option<String>) -> Self { - let Some(phase) = phase else { - return Self::Initial; - }; - - match &*phase.to_ascii_lowercase() { - "initial" => Self::Initial, - "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, - "optimized" => Self::Optimized, - _ => bug!("Unknown runtime phase: '{}'", phase), - } - } } /// Where a specific `mir::Body` comes from. diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index e5864660575..533066bdef9 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::fmt; use std::hash::Hash; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -10,9 +9,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHas use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash128; use rustc_hir::ItemId; -use rustc_hir::attrs::InlineAttr; +use rustc_hir::attrs::{InlineAttr, Linkage}; use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; -use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; @@ -151,7 +149,7 @@ impl<'tcx> MonoItem<'tcx> { // instantiation: // We emit an unused_attributes lint for this case, which should be kept in sync if possible. let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def); - if codegen_fn_attrs.contains_extern_indicator() + if codegen_fn_attrs.contains_extern_indicator(tcx, instance.def.def_id()) || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { return InstantiationMode::GloballyShared { may_conflict: false }; @@ -337,7 +335,6 @@ impl ToStableHashKey<StableHashingContext<'_>> for MonoItem<'_> { pub struct MonoItemPartitions<'tcx> { pub codegen_units: &'tcx [CodegenUnit<'tcx>], pub all_mono_items: &'tcx DefIdSet, - pub autodiff_items: &'tcx [AutoDiffItem], } #[derive(Debug, HashStable)] @@ -369,22 +366,6 @@ pub struct MonoItemData { pub size_estimate: usize, } -/// Specifies the linkage type for a `MonoItem`. -/// -/// See <https://llvm.org/docs/LangRef.html#linkage-types> for more details about these variants. -#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] -pub enum Linkage { - External, - AvailableExternally, - LinkOnceAny, - LinkOnceODR, - WeakAny, - WeakODR, - Internal, - ExternalWeak, - Common, -} - /// Specifies the symbol visibility with regards to dynamic linking. /// /// Visibility doesn't have any effect when linkage is internal. @@ -526,45 +507,68 @@ impl<'tcx> CodegenUnit<'tcx> { tcx: TyCtxt<'tcx>, ) -> Vec<(MonoItem<'tcx>, MonoItemData)> { // The codegen tests rely on items being process in the same order as - // they appear in the file, so for local items, we sort by node_id first + // they appear in the file, so for local items, we sort by span first #[derive(PartialEq, Eq, PartialOrd, Ord)] - struct ItemSortKey<'tcx>(Option<usize>, SymbolName<'tcx>); - + struct ItemSortKey<'tcx>(Option<Span>, SymbolName<'tcx>); + + // We only want to take HirIds of user-defines instances into account. + // The others don't matter for the codegen tests and can even make item + // order unstable. + fn local_item_id<'tcx>(item: MonoItem<'tcx>) -> Option<DefId> { + match item { + MonoItem::Fn(ref instance) => match instance.def { + InstanceKind::Item(def) => def.as_local().map(|_| def), + InstanceKind::VTableShim(..) + | InstanceKind::ReifyShim(..) + | InstanceKind::Intrinsic(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::Virtual(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::CloneShim(..) + | InstanceKind::ThreadLocalShim(..) + | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlue(..) + | InstanceKind::FutureDropPollShim(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => None, + }, + MonoItem::Static(def_id) => def_id.as_local().map(|_| def_id), + MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.def_id.to_def_id()), + } + } fn item_sort_key<'tcx>(tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>) -> ItemSortKey<'tcx> { ItemSortKey( - match item { - MonoItem::Fn(ref instance) => { - match instance.def { - // We only want to take HirIds of user-defined - // instances into account. The others don't matter for - // the codegen tests and can even make item order - // unstable. - InstanceKind::Item(def) => def.as_local().map(Idx::index), - InstanceKind::VTableShim(..) - | InstanceKind::ReifyShim(..) - | InstanceKind::Intrinsic(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::Virtual(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::AsyncDropGlue(..) - | InstanceKind::FutureDropPollShim(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => None, - } - } - MonoItem::Static(def_id) => def_id.as_local().map(Idx::index), - MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.def_id.index()), - }, + local_item_id(item) + .map(|def_id| tcx.def_span(def_id).find_ancestor_not_from_macro()) + .flatten(), item.symbol_name(tcx), ) } let mut items: Vec<_> = self.items().iter().map(|(&i, &data)| (i, data)).collect(); - items.sort_by_cached_key(|&(i, _)| item_sort_key(tcx, i)); + if !tcx.sess.opts.unstable_opts.codegen_source_order { + // In this case, we do not need to keep the items in any specific order, as the input + // is already deterministic. + // + // However, it seems that moving related things (such as different + // monomorphizations of the same function) close to one another is actually beneficial + // for LLVM performance. + // LLVM will codegen the items in the order we pass them to it, and when it handles + // similar things in succession, it seems that it leads to better cache utilization, + // less branch mispredictions and in general to better performance. + // For example, if we have functions `a`, `c::<u32>`, `b`, `c::<i16>`, `d` and + // `c::<bool>`, it seems that it helps LLVM's performance to codegen the three `c` + // instantiations right after one another, as they will likely reference similar types, + // call similar functions, etc. + // + // See https://github.com/rust-lang/rust/pull/145358 for more details. + // + // Sorting by symbol name should not incur any new non-determinism. + items.sort_by_cached_key(|&(i, _)| i.symbol_name(tcx)); + } else { + items.sort_by_cached_key(|&(i, _)| item_sort_key(tcx, i)); + } items } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 809cdb329f7..128ae8549f7 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -585,12 +585,7 @@ fn write_coverage_info_hi( coverage_info_hi: &coverage::CoverageInfoHi, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::CoverageInfoHi { - num_block_markers: _, - branch_spans, - mcdc_degraded_branch_spans, - mcdc_spans, - } = coverage_info_hi; + let coverage::CoverageInfoHi { num_block_markers: _, branch_spans } = coverage_info_hi; // Only add an extra trailing newline if we printed at least one thing. let mut did_print = false; @@ -603,38 +598,6 @@ fn write_coverage_info_hi( did_print = true; } - for coverage::MCDCBranchSpan { span, true_marker, false_marker, .. } in - mcdc_degraded_branch_spans - { - writeln!( - w, - "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", - )?; - did_print = true; - } - - for ( - coverage::MCDCDecisionSpan { span, end_markers, decision_depth, num_conditions: _ }, - conditions, - ) in mcdc_spans - { - let num_conditions = conditions.len(); - writeln!( - w, - "{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}" - )?; - for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in - conditions - { - writeln!( - w, - "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", - condition_info.condition_id - )?; - } - did_print = true; - } - if did_print { writeln!(w)?; } @@ -966,15 +929,18 @@ impl<'tcx> TerminatorKind<'tcx> { } Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"), Unreachable => write!(fmt, "unreachable"), - Drop { place, .. } => write!(fmt, "drop({place:?})"), + Drop { place, async_fut: None, .. } => write!(fmt, "drop({place:?})"), + Drop { place, async_fut: Some(async_fut), .. } => { + write!(fmt, "async drop({place:?}; poll={async_fut:?})") + } Call { func, args, destination, .. } => { write!(fmt, "{destination:?} = ")?; write!(fmt, "{func:?}(")?; - for (index, arg) in args.iter().map(|a| &a.node).enumerate() { + for (index, arg) in args.iter().enumerate() { if index > 0 { write!(fmt, ", ")?; } - write!(fmt, "{arg:?}")?; + write!(fmt, "{:?}", arg.node)?; } write!(fmt, ")") } @@ -984,7 +950,7 @@ impl<'tcx> TerminatorKind<'tcx> { if index > 0 { write!(fmt, ", ")?; } - write!(fmt, "{:?}", arg)?; + write!(fmt, "{:?}", arg.node)?; } write!(fmt, ")") } @@ -1197,8 +1163,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { ty::tls::with(|tcx| { let variant_def = &tcx.adt_def(adt_did).variant(variant); let args = tcx.lift(args).expect("could not lift for printing"); - let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| { - cx.print_def_path(variant_def.def_id, args) + let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| { + p.print_def_path(variant_def.def_id, args) })?; match variant_def.ctor_kind() { @@ -1473,9 +1439,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { }; let fmt_valtree = |cv: &ty::Value<'tcx>| { - let mut cx = FmtPrinter::new(self.tcx, Namespace::ValueNS); - cx.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap(); - cx.into_buffer() + let mut p = FmtPrinter::new(self.tcx, Namespace::ValueNS); + p.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap(); + p.into_buffer() }; let val = match const_ { @@ -1826,7 +1792,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( ascii.push('╼'); i += ptr_size; } - } else if let Some(prov) = alloc.provenance().get(i, &tcx) { + } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) { // Memory with provenance must be defined assert!( alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok() @@ -1836,7 +1802,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Format is similar to "oversized" above. let j = i.bytes_usize(); let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "╾{c:02x}{prov:#?} (1 ptr byte)╼")?; + write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?; i += Size::from_bytes(1); } else if alloc .init_mask() @@ -1967,10 +1933,10 @@ fn pretty_print_const_value_tcx<'tcx>( .expect("destructed mir constant of adt without variant idx"); let variant_def = &def.variant(variant_idx); let args = tcx.lift(args).unwrap(); - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - cx.print_value_path(variant_def.def_id, args)?; - fmt.write_str(&cx.into_buffer())?; + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + p.print_alloc_ids = true; + p.pretty_print_value_path(variant_def.def_id, args)?; + fmt.write_str(&p.into_buffer())?; match variant_def.ctor_kind() { Some(CtorKind::Const) => {} @@ -2001,18 +1967,18 @@ fn pretty_print_const_value_tcx<'tcx>( } } (ConstValue::Scalar(scalar), _) => { - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + p.print_alloc_ids = true; let ty = tcx.lift(ty).unwrap(); - cx.pretty_print_const_scalar(scalar, ty)?; - fmt.write_str(&cx.into_buffer())?; + p.pretty_print_const_scalar(scalar, ty)?; + fmt.write_str(&p.into_buffer())?; return Ok(()); } (ConstValue::ZeroSized, ty::FnDef(d, s)) => { - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - cx.print_value_path(*d, s)?; - fmt.write_str(&cx.into_buffer())?; + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + p.print_alloc_ids = true; + p.pretty_print_value_path(*d, s)?; + fmt.write_str(&p.into_buffer())?; return Ok(()); } // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 683d7b48617..6e52bc775ef 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -160,7 +160,11 @@ impl<'tcx> PlaceTy<'tcx> { /// Convenience wrapper around `projection_ty_core` for `PlaceElem`, /// where we can just use the `Ty` that is already stored inline on /// field projection elems. - pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { + pub fn projection_ty<V: ::std::fmt::Debug>( + self, + tcx: TyCtxt<'tcx>, + elem: ProjectionElem<V, Ty<'tcx>>, + ) -> PlaceTy<'tcx> { self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty) } @@ -290,6 +294,36 @@ impl<V, T> ProjectionElem<V, T> { Self::UnwrapUnsafeBinder(..) => false, } } + + /// Returns the `ProjectionKind` associated to this projection. + pub fn kind(self) -> ProjectionKind { + self.try_map(|_| Some(()), |_| ()).unwrap() + } + + /// Apply functions to types and values in this projection and return the result. + pub fn try_map<V2, T2>( + self, + v: impl FnOnce(V) -> Option<V2>, + t: impl FnOnce(T) -> T2, + ) -> Option<ProjectionElem<V2, T2>> { + Some(match self { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Downcast(name, read_variant) => { + ProjectionElem::Downcast(name, read_variant) + } + ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, t(ty)), + ProjectionElem::ConstantIndex { offset, min_length, from_end } => { + ProjectionElem::ConstantIndex { offset, min_length, from_end } + } + ProjectionElem::Subslice { from, to, from_end } => { + ProjectionElem::Subslice { from, to, from_end } + } + ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(t(ty)), + ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(t(ty)), + ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(t(ty)), + ProjectionElem::Index(val) => ProjectionElem::Index(v(val)?), + }) + } } /// Alias for projections as they appear in `UserTypeProjection`, where we diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6039a03aa29..3b8def67f92 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1371,12 +1371,7 @@ pub enum Rvalue<'tcx> { /// Creates an array where each element is the value of the operand. /// - /// This is the cause of a bug in the case where the repetition count is zero because the value - /// is not dropped, see [#74836]. - /// /// Corresponds to source code like `[x; 32]`. - /// - /// [#74836]: https://github.com/rust-lang/rust/issues/74836 Repeat(Operand<'tcx>, ty::Const<'tcx>), /// Creates a reference of the indicated kind to the place. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 929ebe1aee1..904d78d69b6 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -531,13 +531,20 @@ macro_rules! make_mir_visitor { unwind: _, replace: _, drop: _, - async_fut: _, + async_fut, } => { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Drop), location ); + if let Some(async_fut) = async_fut { + self.visit_local( + $(&$mutability)? *async_fut, + PlaceContext::MutatingUse(MutatingUseContext::Borrow), + location + ); + } } TerminatorKind::Call { @@ -1205,18 +1212,19 @@ macro_rules! visit_place_fns { self.super_projection_elem(place_ref, elem, context, location); } - fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - let mut context = context; - - if !place.projection.is_empty() { - if context.is_use() { - // ^ Only change the context if it is a real use, not a "use" in debuginfo. - context = if context.is_mutating_use() { - PlaceContext::MutatingUse(MutatingUseContext::Projection) - } else { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) - }; - } + fn super_place( + &mut self, + place: &Place<'tcx>, + mut context: PlaceContext, + location: Location, + ) { + if !place.projection.is_empty() && context.is_use() { + // ^ Only change the context if it is a real use, not a "use" in debuginfo. + context = if context.is_mutating_use() { + PlaceContext::MutatingUse(MutatingUseContext::Projection) + } else { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) + }; } self.visit_local(place.local, context, location); @@ -1239,7 +1247,7 @@ macro_rules! visit_place_fns { &mut self, _place_ref: PlaceRef<'tcx>, elem: PlaceElem<'tcx>, - _context: PlaceContext, + context: PlaceContext, location: Location, ) { match elem { @@ -1252,7 +1260,12 @@ macro_rules! visit_place_fns { ProjectionElem::Index(local) => { self.visit_local( local, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + if context.is_use() { + // ^ Only change the context if it is a real use, not a "use" in debuginfo. + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) + } else { + context + }, location, ); } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index a8b357bf105..ea62461ebeb 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -343,6 +343,7 @@ trivial! { rustc_span::Symbol, rustc_span::Ident, rustc_target::spec::PanicStrategy, + rustc_target::spec::SanitizerSet, rustc_type_ir::Variance, u32, usize, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 2941808e806..7bd8a0525a2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -100,7 +100,7 @@ use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_target::spec::PanicStrategy; +use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; @@ -696,6 +696,22 @@ rustc_queries! { return_result_from_ensure_ok } + /// Used in case `mir_borrowck` fails to prove an obligation. We generally assume that + /// all goals we prove in MIR type check hold as we've already checked them in HIR typeck. + /// + /// However, we replace each free region in the MIR body with a unique region inference + /// variable. As we may rely on structural identity when proving goals this may cause a + /// goal to no longer hold. We store obligations for which this may happen during HIR + /// typeck in the `TypeckResults`. We then uniquify and reprove them in case MIR typeck + /// encounters an unexpected error. We expect this to result in an error when used and + /// delay a bug if it does not. + query check_potentially_region_dependent_goals(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { + |tcx| "reproving potentially region dependent HIR typeck goals for `{}", + tcx.def_path_str(key) + } + } + /// MIR after our optimization passes have run. This is MIR that is ready /// for codegen. This is also the only query that can fetch non-local MIR, at present. query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { @@ -1116,6 +1132,11 @@ rustc_queries! { } /// Unsafety-check this `LocalDefId`. + query check_transmutes(key: LocalDefId) { + desc { |tcx| "check transmute calls inside `{}`", tcx.def_path_str(key) } + } + + /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } } @@ -1178,11 +1199,10 @@ rustc_queries! { /// Return the live symbols in the crate for dead code check. /// - /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone) and - /// their respective impl (i.e., part of the derive macro) + /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx ( LocalDefIdSet, - LocalDefIdMap<FxIndexSet<(DefId, DefId)>> + LocalDefIdMap<FxIndexSet<DefId>>, ) { arena_cache desc { "finding live symbols in crate" } @@ -1865,6 +1885,12 @@ rustc_queries! { feedable } + /// Returns whether the field corresponding to the `DefId` has a default field value. + query default_field(def_id: DefId) -> Option<DefId> { + desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } return_result_from_ensure_ok @@ -2681,6 +2707,16 @@ rustc_queries! { desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + + /// Checks for the nearest `#[sanitize(xyz = "off")]` or + /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the + /// crate root. + /// + /// Returns the set of sanitizers that is explicitly disabled for this def. + query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet { + desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } + feedable + } } rustc_with_all_queries! { define_callbacks! } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index a7ac3442898..546791135ba 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -645,34 +645,29 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { let parent = Option::<LocalDefId>::decode(self); let tag: u8 = Decodable::decode(self); - if tag == TAG_PARTIAL_SPAN { - return Span::new(BytePos(0), BytePos(0), ctxt, parent); - } else if tag == TAG_RELATIVE_SPAN { - let dlo = u32::decode(self); - let dto = u32::decode(self); - - let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); - let span = Span::new( - enclosing.lo + BytePos::from_u32(dlo), - enclosing.lo + BytePos::from_u32(dto), - ctxt, - parent, - ); - - return span; - } else { - debug_assert_eq!(tag, TAG_FULL_SPAN); - } - - let file_lo_index = SourceFileIndex::decode(self); - let line_lo = usize::decode(self); - let col_lo = RelativeBytePos::decode(self); - let len = BytePos::decode(self); - - let file_lo = self.file_index_to_file(file_lo_index); - let lo = file_lo.lines()[line_lo - 1] + col_lo; - let lo = file_lo.absolute_position(lo); - let hi = lo + len; + let (lo, hi) = match tag { + TAG_PARTIAL_SPAN => (BytePos(0), BytePos(0)), + TAG_RELATIVE_SPAN => { + let dlo = u32::decode(self); + let dto = u32::decode(self); + + let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); + (enclosing.lo + BytePos::from_u32(dlo), enclosing.lo + BytePos::from_u32(dto)) + } + TAG_FULL_SPAN => { + let file_lo_index = SourceFileIndex::decode(self); + let line_lo = usize::decode(self); + let col_lo = RelativeBytePos::decode(self); + let len = BytePos::decode(self); + + let file_lo = self.file_index_to_file(file_lo_index); + let lo = file_lo.lines()[line_lo - 1] + col_lo; + let lo = file_lo.absolute_position(lo); + let hi = lo + len; + (lo, hi) + } + _ => unreachable!(), + }; Span::new(lo, hi, ctxt, parent) } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3dd6d2c8928..f1d19800a78 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -832,17 +832,17 @@ pub enum PatKind<'tcx> { }, /// One of the following: - /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus + /// * `&str`, which will be handled as a string pattern and thus /// exhaustiveness checking will detect if you use the same string twice in different /// patterns. - /// * integer, bool, char or float (represented as a valtree), which will be handled by + /// * integer, bool, char or float, which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. /// * raw pointers derived from integers, other raw pointers will have already resulted in an // error. /// * `String`, if `string_deref_patterns` is enabled. Constant { - value: mir::Const<'tcx>, + value: ty::Value<'tcx>, }, /// Pattern obtained by converting a constant (inline or named) to its pattern @@ -935,7 +935,7 @@ impl<'tcx> PatRange<'tcx> { let lo_is_min = match self.lo { PatRangeBoundary::NegInfinity => true, PatRangeBoundary::Finite(value) => { - let lo = value.try_to_bits(size).unwrap() ^ bias; + let lo = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias; lo <= min } PatRangeBoundary::PosInfinity => false, @@ -944,7 +944,7 @@ impl<'tcx> PatRange<'tcx> { let hi_is_max = match self.hi { PatRangeBoundary::NegInfinity => false, PatRangeBoundary::Finite(value) => { - let hi = value.try_to_bits(size).unwrap() ^ bias; + let hi = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias; hi > max || hi == max && self.end == RangeEnd::Included } PatRangeBoundary::PosInfinity => true, @@ -957,22 +957,17 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn contains( - &self, - value: mir::Const<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<bool> { + pub fn contains(&self, value: ty::Value<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> { use Ordering::*; - debug_assert_eq!(self.ty, value.ty()); + debug_assert_eq!(value.ty, self.ty); let ty = self.ty; - let value = PatRangeBoundary::Finite(value); + let value = PatRangeBoundary::Finite(value.valtree); // For performance, it's important to only do the second comparison if necessary. Some( - match self.lo.compare_with(value, ty, tcx, typing_env)? { + match self.lo.compare_with(value, ty, tcx)? { Less | Equal => true, Greater => false, - } && match value.compare_with(self.hi, ty, tcx, typing_env)? { + } && match value.compare_with(self.hi, ty, tcx)? { Less => true, Equal => self.end == RangeEnd::Included, Greater => false, @@ -981,21 +976,16 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn overlaps( - &self, - other: &Self, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<bool> { + pub fn overlaps(&self, other: &Self, tcx: TyCtxt<'tcx>) -> Option<bool> { use Ordering::*; debug_assert_eq!(self.ty, other.ty); // For performance, it's important to only do the second comparison if necessary. Some( - match other.lo.compare_with(self.hi, self.ty, tcx, typing_env)? { + match other.lo.compare_with(self.hi, self.ty, tcx)? { Less => true, Equal => self.end == RangeEnd::Included, Greater => false, - } && match self.lo.compare_with(other.hi, self.ty, tcx, typing_env)? { + } && match self.lo.compare_with(other.hi, self.ty, tcx)? { Less => true, Equal => other.end == RangeEnd::Included, Greater => false, @@ -1006,11 +996,13 @@ impl<'tcx> PatRange<'tcx> { impl<'tcx> fmt::Display for PatRange<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let PatRangeBoundary::Finite(value) = &self.lo { + if let &PatRangeBoundary::Finite(valtree) = &self.lo { + let value = ty::Value { ty: self.ty, valtree }; write!(f, "{value}")?; } - if let PatRangeBoundary::Finite(value) = &self.hi { + if let &PatRangeBoundary::Finite(valtree) = &self.hi { write!(f, "{}", self.end)?; + let value = ty::Value { ty: self.ty, valtree }; write!(f, "{value}")?; } else { // `0..` is parsed as an inclusive range, we must display it correctly. @@ -1024,7 +1016,8 @@ impl<'tcx> fmt::Display for PatRange<'tcx> { /// If present, the const must be of a numeric type. #[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)] pub enum PatRangeBoundary<'tcx> { - Finite(mir::Const<'tcx>), + /// The type of this valtree is stored in the surrounding `PatRange`. + Finite(ty::ValTree<'tcx>), NegInfinity, PosInfinity, } @@ -1035,20 +1028,15 @@ impl<'tcx> PatRangeBoundary<'tcx> { matches!(self, Self::Finite(..)) } #[inline] - pub fn as_finite(self) -> Option<mir::Const<'tcx>> { + pub fn as_finite(self) -> Option<ty::ValTree<'tcx>> { match self { Self::Finite(value) => Some(value), Self::NegInfinity | Self::PosInfinity => None, } } - pub fn eval_bits( - self, - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> u128 { + pub fn to_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 { match self { - Self::Finite(value) => value.eval_bits(tcx, typing_env), + Self::Finite(value) => value.try_to_scalar_int().unwrap().to_bits_unchecked(), Self::NegInfinity => { // Unwrap is ok because the type is known to be numeric. ty.numeric_min_and_max_as_bits(tcx).unwrap().0 @@ -1060,14 +1048,8 @@ impl<'tcx> PatRangeBoundary<'tcx> { } } - #[instrument(skip(tcx, typing_env), level = "debug", ret)] - pub fn compare_with( - self, - other: Self, - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option<Ordering> { + #[instrument(skip(tcx), level = "debug", ret)] + pub fn compare_with(self, other: Self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Ordering> { use PatRangeBoundary::*; match (self, other) { // When comparing with infinities, we must remember that `0u8..` and `0u8..=255` @@ -1095,8 +1077,8 @@ impl<'tcx> PatRangeBoundary<'tcx> { _ => {} } - let a = self.eval_bits(ty, tcx, typing_env); - let b = other.eval_bits(ty, tcx, typing_env); + let a = self.to_bits(ty, tcx); + let b = other.to_bits(ty, tcx); match ty.kind() { ty::Float(ty::FloatTy::F16) => { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 5bdde3a514e..32f91bfba6b 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -760,6 +760,9 @@ pub enum DynCompatibilityViolation { // Supertrait has a non-lifetime `for<T>` binder. SupertraitNonLifetimeBinder(SmallVec<[Span; 1]>), + // Trait has a `const Trait` supertrait. + SupertraitConst(SmallVec<[Span; 1]>), + /// Method has something illegal. Method(Symbol, MethodViolationCode, Span), @@ -785,6 +788,9 @@ impl DynCompatibilityViolation { DynCompatibilityViolation::SupertraitNonLifetimeBinder(_) => { "where clause cannot reference non-lifetime `for<...>` variables".into() } + DynCompatibilityViolation::SupertraitConst(_) => { + "it cannot have a `const` supertrait".into() + } DynCompatibilityViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { format!("associated function `{name}` has no `self` parameter").into() } @@ -842,7 +848,8 @@ impl DynCompatibilityViolation { match self { DynCompatibilityViolation::SizedSelf(_) | DynCompatibilityViolation::SupertraitSelf(_) - | DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) => { + | DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) + | DynCompatibilityViolation::SupertraitConst(_) => { DynCompatibilityViolationSolution::None } DynCompatibilityViolation::Method( @@ -873,15 +880,17 @@ impl DynCompatibilityViolation { match self { DynCompatibilityViolation::SupertraitSelf(spans) | DynCompatibilityViolation::SizedSelf(spans) - | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) => spans.clone(), + | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) + | DynCompatibilityViolation::SupertraitConst(spans) => spans.clone(), DynCompatibilityViolation::AssocConst(_, span) | DynCompatibilityViolation::GAT(_, span) - | DynCompatibilityViolation::Method(_, _, span) - if *span != DUMMY_SP => - { - smallvec![*span] + | DynCompatibilityViolation::Method(_, _, span) => { + if *span != DUMMY_SP { + smallvec![*span] + } else { + smallvec![] + } } - _ => smallvec![], } } } diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index d95006dcf4a..a14e47d7082 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -2,10 +2,12 @@ use std::fmt; use std::ops::Deref; use rustc_data_structures::intern::Interned; +use rustc_hir::def::Namespace; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use super::ScalarInt; use crate::mir::interpret::{ErrorHandled, Scalar}; +use crate::ty::print::{FmtPrinter, PrettyPrinter}; use crate::ty::{self, Ty, TyCtxt}; /// This datastructure is used to represent the value of constants used in the type system. @@ -133,6 +135,8 @@ pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, Er /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. +/// Note that this is also used by pattern elaboration to represent values which cannot occur in types, +/// such as raw pointers and floats. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)] pub struct Value<'tcx> { @@ -203,3 +207,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> { self.valtree } } + +impl<'tcx> fmt::Display for Value<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(move |tcx| { + let cv = tcx.lift(*self).unwrap(); + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + p.pretty_print_const_valtree(cv, /*print_ty*/ true)?; + f.write_str(&p.into_buffer()) + }) + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index db56082c71a..4990a85466e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -33,7 +33,7 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan, }; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; @@ -290,11 +290,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); let trait_def_id = self.parent(def_id); debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); - let trait_generics = self.generics_of(trait_def_id); - ( - ty::TraitRef::new_from_args(self, trait_def_id, args.truncate_to(self, trait_generics)), - &args[trait_generics.count()..], - ) + let trait_ref = ty::TraitRef::from_assoc(self, trait_def_id, args); + (trait_ref, &args[trait_ref.args.len()..]) } fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> { @@ -448,7 +445,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } fn fn_is_const(self, def_id: DefId) -> bool { - debug_assert_matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); + debug_assert_matches!( + self.def_kind(def_id), + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) + ); self.is_conditionally_const(def_id) } @@ -1421,6 +1421,8 @@ pub struct TyCtxt<'tcx> { } impl<'tcx> LintEmitter for TyCtxt<'tcx> { + type Id = HirId; + fn emit_node_span_lint( self, lint: &'static Lint, diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index b122ada0925..b9a6f67ab0d 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -7,14 +7,14 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{ Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, listify, pluralize, }; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; use crate::ty::{ - self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, + self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Instance, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; @@ -23,7 +23,16 @@ impl IntoDiagArg for Ty<'_> { fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue { ty::tls::with(|tcx| { let ty = tcx.short_string(self, path); - rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(ty)) + DiagArgValue::Str(std::borrow::Cow::Owned(ty)) + }) + } +} + +impl IntoDiagArg for Instance<'_> { + fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue { + ty::tls::with(|tcx| { + let instance = tcx.short_string_namespace(self, path, Namespace::ValueNS); + DiagArgValue::Str(std::borrow::Cow::Owned(instance)) }) } } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 13723874ad3..3f854038651 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -160,7 +160,11 @@ impl<'tcx> Ty<'tcx> { _ => { let width = tcx.sess.diagnostic_width(); let length_limit = std::cmp::max(width / 4, 40); - format!("`{}`", tcx.string_with_limit(self, length_limit)).into() + format!( + "`{}`", + tcx.string_with_limit(self, length_limit, hir::def::Namespace::TypeNS) + ) + .into() } } } @@ -213,13 +217,13 @@ impl<'tcx> Ty<'tcx> { } impl<'tcx> TyCtxt<'tcx> { - pub fn string_with_limit<T>(self, p: T, length_limit: usize) -> String + pub fn string_with_limit<T>(self, t: T, length_limit: usize, ns: hir::def::Namespace) -> String where T: Copy + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>, { let mut type_limit = 50; - let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| { - self.lift(p).expect("could not lift for printing").print(cx) + let regular = FmtPrinter::print_string(self, ns, |p| { + self.lift(t).expect("could not lift for printing").print(p) }) .expect("could not write to `String`"); if regular.len() <= length_limit { @@ -229,16 +233,12 @@ impl<'tcx> TyCtxt<'tcx> { loop { // Look for the longest properly trimmed path that still fits in length_limit. short = with_forced_trimmed_paths!({ - let mut cx = FmtPrinter::new_with_limit( - self, - hir::def::Namespace::TypeNS, - rustc_session::Limit(type_limit), - ); - self.lift(p) + let mut p = FmtPrinter::new_with_limit(self, ns, rustc_session::Limit(type_limit)); + self.lift(t) .expect("could not lift for printing") - .print(&mut cx) + .print(&mut p) .expect("could not print type"); - cx.into_buffer() + p.into_buffer() }); if short.len() <= length_limit || type_limit == 0 { break; @@ -251,13 +251,29 @@ impl<'tcx> TyCtxt<'tcx> { /// When calling this after a `Diag` is constructed, the preferred way of doing so is /// `tcx.short_string(ty, diag.long_ty_path())`. The diagnostic itself is the one that keeps /// the existence of a "long type" anywhere in the diagnostic, so the note telling the user + /// where we wrote the file to is only printed once. The path will use the type namespace. + pub fn short_string<T>(self, t: T, path: &mut Option<PathBuf>) -> String + where + T: Copy + Hash + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>, + { + self.short_string_namespace(t, path, hir::def::Namespace::TypeNS) + } + + /// When calling this after a `Diag` is constructed, the preferred way of doing so is + /// `tcx.short_string(ty, diag.long_ty_path())`. The diagnostic itself is the one that keeps + /// the existence of a "long type" anywhere in the diagnostic, so the note telling the user /// where we wrote the file to is only printed once. - pub fn short_string<T>(self, p: T, path: &mut Option<PathBuf>) -> String + pub fn short_string_namespace<T>( + self, + t: T, + path: &mut Option<PathBuf>, + namespace: hir::def::Namespace, + ) -> String where T: Copy + Hash + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>, { - let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| { - self.lift(p).expect("could not lift for printing").print(cx) + let regular = FmtPrinter::print_string(self, namespace, |p| { + self.lift(t).expect("could not lift for printing").print(p) }) .expect("could not write to `String`"); @@ -270,13 +286,13 @@ impl<'tcx> TyCtxt<'tcx> { if regular.len() <= width * 2 / 3 { return regular; } - let short = self.string_with_limit(p, length_limit); + let short = self.string_with_limit(t, length_limit, namespace); if regular == short { return regular; } // Ensure we create an unique file for the type passed in when we create a file. let mut s = DefaultHasher::new(); - p.hash(&mut s); + t.hash(&mut s); let hash = s.finish(); *path = Some(path.take().unwrap_or_else(|| { self.output_filenames(()).temp_path_for_diagnostic(&format!("long-type-{hash}.txt")) diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index b02abb5ab43..3ade3a3ef51 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -527,21 +527,28 @@ impl<'tcx> GenericArgs<'tcx> { #[inline] #[track_caller] pub fn type_at(&self, i: usize) -> Ty<'tcx> { - self[i].as_type().unwrap_or_else(|| bug!("expected type for param #{} in {:?}", i, self)) + self[i].as_type().unwrap_or_else( + #[track_caller] + || bug!("expected type for param #{} in {:?}", i, self), + ) } #[inline] #[track_caller] pub fn region_at(&self, i: usize) -> ty::Region<'tcx> { - self[i] - .as_region() - .unwrap_or_else(|| bug!("expected region for param #{} in {:?}", i, self)) + self[i].as_region().unwrap_or_else( + #[track_caller] + || bug!("expected region for param #{} in {:?}", i, self), + ) } #[inline] #[track_caller] pub fn const_at(&self, i: usize) -> ty::Const<'tcx> { - self[i].as_const().unwrap_or_else(|| bug!("expected const for param #{} in {:?}", i, self)) + self[i].as_const().unwrap_or_else( + #[track_caller] + || bug!("expected const for param #{} in {:?}", i, self), + ) } #[inline] @@ -578,6 +585,9 @@ impl<'tcx> GenericArgs<'tcx> { tcx.mk_args_from_iter(target_args.iter().chain(self.iter().skip(defs.count()))) } + /// Truncates this list of generic args to have at most the number of args in `generics`. + /// + /// You might be looking for [`TraitRef::from_assoc`](super::TraitRef::from_assoc). pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> GenericArgsRef<'tcx> { tcx.mk_args(&self[..generics.count()]) } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index eb35a952032..3a51f79f121 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,6 +1,5 @@ use std::assert_matches::assert_matches; use std::fmt; -use std::path::PathBuf; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; @@ -17,7 +16,7 @@ use tracing::{debug, instrument}; use crate::error; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::normalize_erasing_regions::NormalizationError; -use crate::ty::print::{FmtPrinter, Printer, shrunk_instance_name}; +use crate::ty::print::{FmtPrinter, Print}; use crate::ty::{ self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -389,59 +388,15 @@ fn type_length<'tcx>(item: impl TypeVisitable<TyCtxt<'tcx>>) -> usize { visitor.type_length } -pub fn fmt_instance( - f: &mut fmt::Formatter<'_>, - instance: Instance<'_>, - type_length: Option<rustc_session::Limit>, -) -> fmt::Result { - ty::tls::with(|tcx| { - let args = tcx.lift(instance.args).expect("could not lift for printing"); - - let mut cx = if let Some(type_length) = type_length { - FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length) - } else { - FmtPrinter::new(tcx, Namespace::ValueNS) - }; - cx.print_def_path(instance.def_id(), args)?; - let s = cx.into_buffer(); - f.write_str(&s) - })?; - - match instance.def { - InstanceKind::Item(_) => Ok(()), - InstanceKind::VTableShim(_) => write!(f, " - shim(vtable)"), - InstanceKind::ReifyShim(_, None) => write!(f, " - shim(reify)"), - InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"), - InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"), - InstanceKind::ThreadLocalShim(_) => write!(f, " - shim(tls)"), - InstanceKind::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceKind::Virtual(_, num) => write!(f, " - virtual#{num}"), - InstanceKind::FnPtrShim(_, ty) => write!(f, " - shim({ty})"), - InstanceKind::ClosureOnceShim { .. } => write!(f, " - shim"), - InstanceKind::ConstructCoroutineInClosureShim { .. } => write!(f, " - shim"), - InstanceKind::DropGlue(_, None) => write!(f, " - shim(None)"), - InstanceKind::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), - InstanceKind::CloneShim(_, ty) => write!(f, " - shim({ty})"), - InstanceKind::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), - InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => { - write!(f, " - dropshim({proxy_ty}-{impl_ty})") - } - InstanceKind::AsyncDropGlue(_, ty) => write!(f, " - shim({ty})"), - InstanceKind::AsyncDropGlueCtorShim(_, ty) => write!(f, " - shim(Some({ty}))"), - } -} - -pub struct ShortInstance<'tcx>(pub Instance<'tcx>, pub usize); - -impl<'tcx> fmt::Display for ShortInstance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_instance(f, self.0, Some(rustc_session::Limit(self.1))) - } -} - impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_instance(f, *self, None) + ty::tls::with(|tcx| { + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + let instance = tcx.lift(*self).expect("could not lift for printing"); + instance.print(&mut p)?; + let s = p.into_buffer(); + f.write_str(&s) + }) } } @@ -610,23 +565,12 @@ impl<'tcx> Instance<'tcx> { Ok(None) => { let type_length = type_length(args); if !tcx.type_length_limit().value_within_limit(type_length) { - let (shrunk, written_to_path) = - shrunk_instance_name(tcx, Instance::new_raw(def_id, args)); - let mut path = PathBuf::new(); - let was_written = if let Some(path2) = written_to_path { - path = path2; - true - } else { - false - }; tcx.dcx().emit_fatal(error::TypeLengthLimit { // We don't use `def_span(def_id)` so that diagnostics point // to the crate root during mono instead of to foreign items. // This is arguably better. span: span_or_local_def_span(), - shrunk, - was_written, - path, + instance: Instance::new_raw(def_id, args), type_length, }); } else { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0deb2482c6f..e567ba05f61 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -32,7 +32,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -46,7 +46,6 @@ use rustc_macros::{ }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; -use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, sym}; @@ -82,7 +81,7 @@ pub use self::context::{ TyCtxtFeed, tls, }; pub use self::fold::*; -pub use self::instance::{Instance, InstanceKind, ReifyReason, ShortInstance, UnusedGenericParams}; +pub use self::instance::{Instance, InstanceKind, ReifyReason, UnusedGenericParams}; pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::opaque_types::OpaqueTypeKey; pub use self::pattern::{Pattern, PatternKind}; @@ -830,14 +829,15 @@ impl<'tcx> OpaqueHiddenType<'tcx> { // Convert the type from the function into a type valid outside by mapping generic // parameters to into the context of the opaque. // - // We erase regions when doing this during HIR typeck. + // We erase regions when doing this during HIR typeck. We manually use `fold_regions` + // here as we do not want to anonymize bound variables. let this = match defining_scope_kind { - DefiningScopeKind::HirTypeck => tcx.erase_regions(self), + DefiningScopeKind::HirTypeck => fold_regions(tcx, self, |_, _| tcx.lifetimes.re_erased), DefiningScopeKind::MirBorrowck => self, }; let result = this.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span)); if cfg!(debug_assertions) && matches!(defining_scope_kind, DefiningScopeKind::HirTypeck) { - assert_eq!(result.ty, tcx.erase_regions(result.ty)); + assert_eq!(result.ty, fold_regions(tcx, result.ty, |_, _| tcx.lifetimes.re_erased)); } result } @@ -1031,6 +1031,8 @@ pub struct ParamEnvAnd<'tcx, T> { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] #[derive(TypeVisitable, TypeFoldable)] pub struct TypingEnv<'tcx> { + #[type_foldable(identity)] + #[type_visitable(ignore)] pub typing_mode: TypingMode<'tcx>, pub param_env: ParamEnv<'tcx>, } @@ -1923,21 +1925,50 @@ impl<'tcx> TyCtxt<'tcx> { self.impl_trait_ref(def_id).map(|tr| tr.skip_binder().def_id) } - /// If the given `DefId` is an associated item, returns the `DefId` of the parent trait or impl. - pub fn assoc_parent(self, def_id: DefId) -> Option<DefId> { - self.def_kind(def_id).is_assoc().then(|| self.parent(def_id)) + /// If the given `DefId` is an associated item, returns the `DefId` and `DefKind` of the parent trait or impl. + pub fn assoc_parent(self, def_id: DefId) -> Option<(DefId, DefKind)> { + if !self.def_kind(def_id).is_assoc() { + return None; + } + let parent = self.parent(def_id); + let def_kind = self.def_kind(parent); + Some((parent, def_kind)) } /// If the given `DefId` is an associated item of a trait, /// returns the `DefId` of the trait; otherwise, returns `None`. pub fn trait_of_assoc(self, def_id: DefId) -> Option<DefId> { - self.assoc_parent(def_id).filter(|id| self.def_kind(id) == DefKind::Trait) + match self.assoc_parent(def_id) { + Some((id, DefKind::Trait)) => Some(id), + _ => None, + } } /// If the given `DefId` is an associated item of an impl, /// returns the `DefId` of the impl; otherwise returns `None`. pub fn impl_of_assoc(self, def_id: DefId) -> Option<DefId> { - self.assoc_parent(def_id).filter(|id| matches!(self.def_kind(id), DefKind::Impl { .. })) + match self.assoc_parent(def_id) { + Some((id, DefKind::Impl { .. })) => Some(id), + _ => None, + } + } + + /// If the given `DefId` is an associated item of an inherent impl, + /// returns the `DefId` of the impl; otherwise, returns `None`. + pub fn inherent_impl_of_assoc(self, def_id: DefId) -> Option<DefId> { + match self.assoc_parent(def_id) { + Some((id, DefKind::Impl { of_trait: false })) => Some(id), + _ => None, + } + } + + /// If the given `DefId` is an associated item of a trait impl, + /// returns the `DefId` of the impl; otherwise, returns `None`. + pub fn trait_impl_of_assoc(self, def_id: DefId) -> Option<DefId> { + match self.assoc_parent(def_id) { + Some((id, DefKind::Impl { of_trait: true })) => Some(id), + _ => None, + } } pub fn is_exportable(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 73a6f1829af..59e00f85957 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -6,8 +6,7 @@ use rustc_macros::{HashStable, extension}; use rustc_type_ir as ir; use crate::ty::{ - self, DebruijnIndex, EarlyBinder, PredicatePolarity, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, - WithCachedTypeInfo, + self, DebruijnIndex, EarlyBinder, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, WithCachedTypeInfo, }; pub type TraitRef<'tcx> = ir::TraitRef<TyCtxt<'tcx>>; @@ -536,15 +535,6 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for Clause } } -impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> { - fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self { - from.map_bound(|trait_ref| TraitPredicate { - trait_ref, - polarity: PredicatePolarity::Positive, - }) - } -} - impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitPredicate<'tcx>> for Predicate<'tcx> { fn upcast_from(from: TraitPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { PredicateKind::Clause(ClauseKind::Trait(from)).upcast(tcx) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 9172c5d3ab7..9e6f277ef77 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use hir::def::Namespace; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; @@ -8,7 +6,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use tracing::{debug, instrument, trace}; -use crate::ty::{self, GenericArg, ShortInstance, Ty, TyCtxt}; +use crate::ty::{self, GenericArg, Ty, TyCtxt}; // `pretty` is a separate module only for organization. mod pretty; @@ -18,21 +16,19 @@ use super::Lift; pub type PrintError = std::fmt::Error; pub trait Print<'tcx, P> { - fn print(&self, cx: &mut P) -> Result<(), PrintError>; + fn print(&self, p: &mut P) -> Result<(), PrintError>; } -/// Interface for outputting user-facing "type-system entities" -/// (paths, types, lifetimes, constants, etc.) as a side-effect -/// (e.g. formatting, like `PrettyPrinter` implementors do) or by -/// constructing some alternative representation (e.g. an AST), -/// which the associated types allow passing through the methods. -/// -/// For pretty-printing/formatting in particular, see `PrettyPrinter`. -// -// FIXME(eddyb) find a better name; this is more general than "printing". +/// A trait that "prints" user-facing type system entities: paths, types, lifetimes, constants, +/// etc. "Printing" here means building up a representation of the entity's path, usually as a +/// `String` (e.g. "std::io::Read") or a `Vec<Symbol>` (e.g. `[sym::std, sym::io, sym::Read]`). The +/// representation is built up by appending one or more pieces. The specific details included in +/// the built-up representation depend on the purpose of the printer. The more advanced printers +/// also rely on the `PrettyPrinter` sub-trait. pub trait Printer<'tcx>: Sized { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + /// Appends a representation of an entity with a normal path, e.g. "std::io::Read". fn print_def_path( &mut self, def_id: DefId, @@ -41,6 +37,7 @@ pub trait Printer<'tcx>: Sized { self.default_print_def_path(def_id, args) } + /// Like `print_def_path`, but for `DefPathData::Impl`. fn print_impl_path( &mut self, impl_def_id: DefId, @@ -66,47 +63,74 @@ pub trait Printer<'tcx>: Sized { self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } + /// Appends a representation of a region. fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a type. fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a list of `PolyExistentialPredicate`s. fn print_dyn_existential( &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Result<(), PrintError>; + /// Appends a representation of a const. fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; + /// Appends a representation of a crate name, e.g. `std`, or even ``. + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError>; - fn path_qualified( + /// Appends a representation of a (full or partial) simple path, in two parts. `print_prefix`, + /// when called, appends the representation of the leading segments. The rest of the method + /// appends the representation of the final segment, the details of which are in + /// `disambiguated_data`. + /// + /// E.g. `std::io` + `Read` -> `std::io::Read`. + fn print_path_with_simple( &mut self, - self_ty: Ty<'tcx>, - trait_ref: Option<ty::TraitRef<'tcx>>, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, + disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError>; - fn path_append_impl( + /// Similar to `print_path_with_simple`, but the final segment is an `impl` segment. + /// + /// E.g. `slice` + `<impl [T]>` -> `slice::<impl [T]>`, which may then be further appended to, + /// giving a longer path representation such as `slice::<impl [T]>::to_vec_in::ConvertVec`. + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<(), PrintError>; - fn path_append( + /// Appends a representation of a path ending in generic args, in two parts. `print_prefix`, + /// when called, appends the leading segments. The rest of the method appends the + /// representation of the generic args. (Some printers choose to skip appending the generic + /// args.) + /// + /// E.g. `ImplementsTraitForUsize` + `<usize>` -> `ImplementsTraitForUsize<usize>`. + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - disambiguated_data: &DisambiguatedDefPathData, + args: &[GenericArg<'tcx>], ) -> Result<(), PrintError>; - fn path_generic_args( + /// Appends a representation of a qualified path segment, e.g. `<OsString as From<&T>>`. + /// If `trait_ref` is `None`, it may fall back to simpler forms, e.g. `<Vec<T>>` or just `Foo`. + fn print_path_with_qualified( &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - args: &[GenericArg<'tcx>], + self_ty: Ty<'tcx>, + trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<(), PrintError>; - fn should_truncate(&mut self) -> bool { - false + fn print_coroutine_with_kind( + &mut self, + def_id: DefId, + parent_args: &'tcx [GenericArg<'tcx>], + kind: Ty<'tcx>, + ) -> Result<(), PrintError> { + self.print_path_with_generic_args(|p| p.print_def_path(def_id, parent_args), &[kind.into()]) } // Defaults (should not be overridden): @@ -123,7 +147,7 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { DefPathData::CrateRoot => { assert!(key.parent.is_none()); - self.path_crate(def_id.krate) + self.print_crate_name(def_id.krate) } DefPathData::Impl => self.print_impl_path(def_id, args), @@ -147,9 +171,10 @@ pub trait Printer<'tcx>: Sized { )) = self.tcx().coroutine_kind(def_id) && args.len() > parent_args.len() { - return self.path_generic_args( - |cx| cx.print_def_path(def_id, parent_args), - &args[..parent_args.len() + 1][..1], + return self.print_coroutine_with_kind( + def_id, + parent_args, + args[parent_args.len()].expect_ty(), ); } else { // Closures' own generics are only captures, don't print them. @@ -169,8 +194,8 @@ pub trait Printer<'tcx>: Sized { _ => { if !generics.is_own_empty() && args.len() >= generics.count() { let args = generics.own_args_no_defaults(self.tcx(), args); - return self.path_generic_args( - |cx| cx.print_def_path(def_id, parent_args), + return self.print_path_with_generic_args( + |p| p.print_def_path(def_id, parent_args), args, ); } @@ -185,17 +210,17 @@ pub trait Printer<'tcx>: Sized { && self.tcx().generics_of(parent_def_id).parent_count == 0; } - self.path_append( - |cx: &mut Self| { + self.print_path_with_simple( + |p: &mut Self| { if trait_qualify_parent { let trait_ref = ty::TraitRef::new( - cx.tcx(), + p.tcx(), parent_def_id, parent_args.iter().copied(), ); - cx.path_qualified(trait_ref.self_ty(), Some(trait_ref)) + p.print_path_with_qualified(trait_ref.self_ty(), Some(trait_ref)) } else { - cx.print_def_path(parent_def_id, parent_args) + p.print_def_path(parent_def_id, parent_args) } }, &key.disambiguated_data, @@ -236,16 +261,15 @@ pub trait Printer<'tcx>: Sized { // If the impl is not co-located with either self-type or // trait-type, then fallback to a format that identifies // the module more clearly. - self.path_append_impl( - |cx| cx.print_def_path(parent_def_id, &[]), - &key.disambiguated_data, + self.print_path_with_impl( + |p| p.print_def_path(parent_def_id, &[]), self_ty, impl_trait_ref, ) } else { // Otherwise, try to give a good form that would be valid language // syntax. Preferably using associated item notation. - self.path_qualified(self_ty, impl_trait_ref) + self.print_path_with_qualified(self_ty, impl_trait_ref) } } } @@ -312,26 +336,63 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'tcx> { - fn print(&self, cx: &mut P) -> Result<(), PrintError> { - cx.print_region(*self) + fn print(&self, p: &mut P) -> Result<(), PrintError> { + p.print_region(*self) } } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { + fn print(&self, p: &mut P) -> Result<(), PrintError> { + p.print_type(*self) + } +} + +impl<'tcx, P: Printer<'tcx> + std::fmt::Write> Print<'tcx, P> for ty::Instance<'tcx> { fn print(&self, cx: &mut P) -> Result<(), PrintError> { - cx.print_type(*self) + cx.print_def_path(self.def_id(), self.args)?; + match self.def { + ty::InstanceKind::Item(_) => {} + ty::InstanceKind::VTableShim(_) => cx.write_str(" - shim(vtable)")?, + ty::InstanceKind::ReifyShim(_, None) => cx.write_str(" - shim(reify)")?, + ty::InstanceKind::ReifyShim(_, Some(ty::ReifyReason::FnPtr)) => { + cx.write_str(" - shim(reify-fnptr)")? + } + ty::InstanceKind::ReifyShim(_, Some(ty::ReifyReason::Vtable)) => { + cx.write_str(" - shim(reify-vtable)")? + } + ty::InstanceKind::ThreadLocalShim(_) => cx.write_str(" - shim(tls)")?, + ty::InstanceKind::Intrinsic(_) => cx.write_str(" - intrinsic")?, + ty::InstanceKind::Virtual(_, num) => cx.write_str(&format!(" - virtual#{num}"))?, + ty::InstanceKind::FnPtrShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, + ty::InstanceKind::ClosureOnceShim { .. } => cx.write_str(" - shim")?, + ty::InstanceKind::ConstructCoroutineInClosureShim { .. } => cx.write_str(" - shim")?, + ty::InstanceKind::DropGlue(_, None) => cx.write_str(" - shim(None)")?, + ty::InstanceKind::DropGlue(_, Some(ty)) => { + cx.write_str(&format!(" - shim(Some({ty}))"))? + } + ty::InstanceKind::CloneShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, + ty::InstanceKind::FnPtrAddrShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, + ty::InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => { + cx.write_str(&format!(" - dropshim({proxy_ty}-{impl_ty})"))? + } + ty::InstanceKind::AsyncDropGlue(_, ty) => cx.write_str(&format!(" - shim({ty})"))?, + ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => { + cx.write_str(&format!(" - shim(Some({ty}))"))? + } + }; + Ok(()) } } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> { - fn print(&self, cx: &mut P) -> Result<(), PrintError> { - cx.print_dyn_existential(self) + fn print(&self, p: &mut P) -> Result<(), PrintError> { + p.print_dyn_existential(self) } } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> { - fn print(&self, cx: &mut P) -> Result<(), PrintError> { - cx.print_const(*self) + fn print(&self, p: &mut P) -> Result<(), PrintError> { + p.print_const(*self) } } @@ -351,9 +412,9 @@ where { fn print(t: &T, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ty::tls::with(|tcx| { - let mut cx = FmtPrinter::new(tcx, Namespace::TypeNS); - tcx.lift(*t).expect("could not lift for printing").print(&mut cx)?; - fmt.write_str(&cx.into_buffer())?; + let mut p = FmtPrinter::new(tcx, Namespace::TypeNS); + tcx.lift(*t).expect("could not lift for printing").print(&mut p)?; + fmt.write_str(&p.into_buffer())?; Ok(()) }) } @@ -362,31 +423,3 @@ where with_no_trimmed_paths!(Self::print(t, fmt)) } } - -/// Format instance name that is already known to be too long for rustc. -/// 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. -pub fn shrunk_instance_name<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::Instance<'tcx>, -) -> (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(33).is_some() { - let shrunk = format!("{}", ShortInstance(instance, 4)); - if shrunk == s { - return (s, None); - } - - let path = tcx.output_filenames(()).temp_path_for_diagnostic("long-type.txt"); - let written_to_path = std::fs::write(&path, s).ok().map(|_| path); - - (shrunk, written_to_path) - } else { - (s, None) - } -} diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 71eac294f15..74caee7336a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -29,33 +29,6 @@ use crate::ty::{ TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -macro_rules! p { - (@$lit:literal) => { - write!(scoped_cx!(), $lit)? - }; - (@write($($data:expr),+)) => { - write!(scoped_cx!(), $($data),+)? - }; - (@print($x:expr)) => { - $x.print(scoped_cx!())? - }; - (@$method:ident($($arg:expr),*)) => { - scoped_cx!().$method($($arg),*)? - }; - ($($elem:tt $(($($args:tt)*))?),+) => {{ - $(p!(@ $elem $(($($args)*))?);)+ - }}; -} -macro_rules! define_scoped_cx { - ($cx:ident) => { - macro_rules! scoped_cx { - () => { - $cx - }; - } - }; -} - thread_local! { static FORCE_IMPL_FILENAME_LINE: Cell<bool> = const { Cell::new(false) }; static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = const { Cell::new(false) }; @@ -272,7 +245,7 @@ impl<'tcx> RegionHighlightMode<'tcx> { /// Trait for printers that pretty-print using `fmt::Write` to the printer. pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// Like `print_def_path` but for value paths. - fn print_value_path( + fn pretty_print_value_path( &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], @@ -280,7 +253,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.print_def_path(def_id, args) } - fn print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn pretty_print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { @@ -360,10 +333,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { f: impl FnOnce(&mut Self) -> Result<(), PrintError>, ) -> Result<(), PrintError>; - /// Returns `true` if the region should be printed in - /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. - /// This is typically the case for all non-`'_` regions. - fn should_print_region(&self, region: ty::Region<'tcx>) -> bool; + fn should_truncate(&mut self) -> bool { + false + } + + /// Returns `true` if the region should be printed in optional positions, + /// e.g., `&'a T` or `dyn Tr + 'b`. (Regions like the one in `Cow<'static, T>` + /// will always be printed.) + fn should_print_optional_region(&self, region: ty::Region<'tcx>) -> bool; fn reset_type_limit(&mut self) {} @@ -497,7 +474,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // path to the crate followed by the path to the item within the crate. if let Some(cnum) = def_id.as_crate_root() { if cnum == LOCAL_CRATE { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } @@ -521,7 +498,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // or avoid ending up with `ExternCrateSource::Extern`, // for the injected `std`/`core`. if span.is_dummy() { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } @@ -535,13 +512,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(true); } (ExternCrateSource::Path, LOCAL_CRATE) => { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } _ => {} }, None => { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } } @@ -651,7 +628,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(false); } callers.push(visible_parent); - // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid + // HACK(eddyb) this bypasses `print_path_with_simple`'s prefix printing to avoid // knowing ahead of time whether the entire path will succeed or not. // To support printers that do not implement `PrettyPrinter`, a `Vec` or // linked list on the stack would need to be built, before any printing. @@ -660,11 +637,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { true => {} } callers.pop(); - self.path_append(|_| Ok(()), &DisambiguatedDefPathData { data, disambiguator: 0 })?; + self.print_path_with_simple( + |_| Ok(()), + &DisambiguatedDefPathData { data, disambiguator: 0 }, + )?; Ok(true) } - fn pretty_path_qualified( + fn pretty_print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, @@ -689,18 +669,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - self.generic_delimiters(|cx| { - define_scoped_cx!(cx); - - p!(print(self_ty)); + self.generic_delimiters(|p| { + self_ty.print(p)?; if let Some(trait_ref) = trait_ref { - p!(" as ", print(trait_ref.print_only_trait_path())); + write!(p, " as ")?; + trait_ref.print_only_trait_path().print(p)?; } Ok(()) }) } - fn pretty_path_append_impl( + fn pretty_print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -708,121 +687,121 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ) -> Result<(), PrintError> { print_prefix(self)?; - self.generic_delimiters(|cx| { - define_scoped_cx!(cx); - - p!("impl "); + self.generic_delimiters(|p| { + write!(p, "impl ")?; if let Some(trait_ref) = trait_ref { - p!(print(trait_ref.print_only_trait_path()), " for "); + trait_ref.print_only_trait_path().print(p)?; + write!(p, " for ")?; } - p!(print(self_ty)); + self_ty.print(p)?; Ok(()) }) } fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { - define_scoped_cx!(self); - match *ty.kind() { - ty::Bool => p!("bool"), - ty::Char => p!("char"), - ty::Int(t) => p!(write("{}", t.name_str())), - ty::Uint(t) => p!(write("{}", t.name_str())), - ty::Float(t) => p!(write("{}", t.name_str())), + ty::Bool => write!(self, "bool")?, + ty::Char => write!(self, "char")?, + ty::Int(t) => write!(self, "{}", t.name_str())?, + ty::Uint(t) => write!(self, "{}", t.name_str())?, + ty::Float(t) => write!(self, "{}", t.name_str())?, ty::Pat(ty, pat) => { - p!("(", print(ty), ") is ", write("{pat:?}")) + write!(self, "(")?; + ty.print(self)?; + write!(self, ") is {pat:?}")?; } ty::RawPtr(ty, mutbl) => { - p!(write("*{} ", mutbl.ptr_str())); - p!(print(ty)) + write!(self, "*{} ", mutbl.ptr_str())?; + ty.print(self)?; } ty::Ref(r, ty, mutbl) => { - p!("&"); - if self.should_print_region(r) { - p!(print(r), " "); + write!(self, "&")?; + if self.should_print_optional_region(r) { + r.print(self)?; + write!(self, " ")?; } - p!(print(ty::TypeAndMut { ty, mutbl })) + ty::TypeAndMut { ty, mutbl }.print(self)?; } - ty::Never => p!("!"), + ty::Never => write!(self, "!")?, ty::Tuple(tys) => { - p!("(", comma_sep(tys.iter())); + write!(self, "(")?; + self.comma_sep(tys.iter())?; if tys.len() == 1 { - p!(","); + write!(self, ",")?; } - p!(")") + write!(self, ")")?; } ty::FnDef(def_id, args) => { if with_reduced_queries() { - p!(print_def_path(def_id, args)); + self.print_def_path(def_id, args)?; } else { let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args); if self.tcx().codegen_fn_attrs(def_id).safe_target_features { - p!("#[target_features] "); + write!(self, "#[target_features] ")?; sig = sig.map_bound(|mut sig| { sig.safety = hir::Safety::Safe; sig }); } - p!(print(sig), " {{", print_value_path(def_id, args), "}}"); + sig.print(self)?; + write!(self, " {{")?; + self.pretty_print_value_path(def_id, args)?; + write!(self, "}}")?; } } - ty::FnPtr(ref sig_tys, hdr) => p!(print(sig_tys.with(hdr))), + ty::FnPtr(ref sig_tys, hdr) => sig_tys.with(hdr).print(self)?, ty::UnsafeBinder(ref bound_ty) => { - self.wrap_binder(bound_ty, WrapBinderMode::Unsafe, |ty, cx| { - cx.pretty_print_type(*ty) + self.wrap_binder(bound_ty, WrapBinderMode::Unsafe, |ty, p| { + p.pretty_print_type(*ty) })?; } ty::Infer(infer_ty) => { if self.should_print_verbose() { - p!(write("{:?}", ty.kind())); + write!(self, "{:?}", ty.kind())?; return Ok(()); } if let ty::TyVar(ty_vid) = infer_ty { if let Some(name) = self.ty_infer_name(ty_vid) { - p!(write("{}", name)) + write!(self, "{name}")?; } else { - p!(write("{}", infer_ty)) + write!(self, "{infer_ty}")?; } } else { - p!(write("{}", infer_ty)) + write!(self, "{infer_ty}")?; } } - ty::Error(_) => p!("{{type error}}"), - ty::Param(ref param_ty) => p!(print(param_ty)), + ty::Error(_) => write!(self, "{{type error}}")?, + ty::Param(ref param_ty) => param_ty.print(self)?, ty::Bound(debruijn, bound_ty) => match bound_ty.kind { ty::BoundTyKind::Anon => { rustc_type_ir::debug_bound_var(self, debruijn, bound_ty.var)? } ty::BoundTyKind::Param(def_id) => match self.should_print_verbose() { - true => p!(write("{:?}", ty.kind())), - false => p!(write("{}", self.tcx().item_name(def_id))), + true => write!(self, "{:?}", ty.kind())?, + false => write!(self, "{}", self.tcx().item_name(def_id))?, }, }, - ty::Adt(def, args) => { - p!(print_def_path(def.did(), args)); - } + ty::Adt(def, args) => self.print_def_path(def.did(), args)?, ty::Dynamic(data, r, repr) => { - let print_r = self.should_print_region(r); + let print_r = self.should_print_optional_region(r); if print_r { - p!("("); + write!(self, "(")?; } match repr { - ty::Dyn => p!("dyn "), + ty::Dyn => write!(self, "dyn ")?, } - p!(print(data)); + data.print(self)?; if print_r { - p!(" + ", print(r), ")"); + write!(self, " + ")?; + r.print(self)?; + write!(self, ")")?; } } - ty::Foreign(def_id) => { - p!(print_def_path(def_id, &[])); - } - ty::Alias(ty::Projection | ty::Inherent | ty::Free, ref data) => { - p!(print(data)) - } - ty::Placeholder(placeholder) => p!(print(placeholder)), + ty::Foreign(def_id) => self.print_def_path(def_id, &[])?, + ty::Alias(ty::Projection | ty::Inherent | ty::Free, ref data) => data.print(self)?, + ty::Placeholder(placeholder) => placeholder.print(self)?, ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // We use verbose printing in 'NO_QUERIES' mode, to // avoid needing to call `predicates_of`. This should @@ -834,7 +813,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // example.] if self.should_print_verbose() { // FIXME(eddyb) print this with `print_def_path`. - p!(write("Opaque({:?}, {})", def_id, args.print_as_list())); + write!(self, "Opaque({:?}, {})", def_id, args.print_as_list())?; return Ok(()); } @@ -849,17 +828,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if d == def_id { // If the type alias directly starts with the `impl` of the // opaque type we're printing, then skip the `::{opaque#1}`. - p!(print_def_path(parent, args)); + self.print_def_path(parent, args)?; return Ok(()); } } // Complex opaque type, e.g. `type Foo = (i32, impl Debug);` - p!(print_def_path(def_id, args)); + self.print_def_path(def_id, args)?; return Ok(()); } _ => { if with_reduced_queries() { - p!(print_def_path(def_id, &[])); + self.print_def_path(def_id, &[])?; return Ok(()); } else { return self.pretty_print_opaque_impl_type(def_id, args); @@ -867,9 +846,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } } - ty::Str => p!("str"), + ty::Str => write!(self, "str")?, ty::Coroutine(did, args) => { - p!("{{"); + write!(self, "{{")?; let coroutine_kind = self.tcx().coroutine_kind(did).unwrap(); let should_print_movability = self.should_print_verbose() || matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_)); @@ -877,12 +856,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if should_print_movability { match coroutine_kind.movability() { hir::Movability::Movable => {} - hir::Movability::Static => p!("static "), + hir::Movability::Static => write!(self, "static ")?, } } if !self.should_print_verbose() { - p!(write("{}", coroutine_kind)); + write!(self, "{coroutine_kind}")?; if coroutine_kind.is_fn_like() { // If we are printing an `async fn` coroutine type, then give the path // of the fn, instead of its span, because that will in most cases be @@ -891,66 +870,71 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // This will look like: // {async fn body of some_fn()} let did_of_the_fn_item = self.tcx().parent(did); - p!(" of ", print_def_path(did_of_the_fn_item, args), "()"); + write!(self, " of ")?; + self.print_def_path(did_of_the_fn_item, args)?; + write!(self, "()")?; } else if let Some(local_did) = did.as_local() { let span = self.tcx().def_span(local_did); - p!(write( + write!( + self, "@{}", // This may end up in stderr diagnostics but it may also be emitted // into MIR. Hence we use the remapped path if available self.tcx().sess.source_map().span_to_embeddable_string(span) - )); + )?; } else { - p!("@", print_def_path(did, args)); + write!(self, "@")?; + self.print_def_path(did, args)?; } } else { - p!(print_def_path(did, args)); - p!( - " upvar_tys=", - print(args.as_coroutine().tupled_upvars_ty()), - " resume_ty=", - print(args.as_coroutine().resume_ty()), - " yield_ty=", - print(args.as_coroutine().yield_ty()), - " return_ty=", - print(args.as_coroutine().return_ty()) - ); + self.print_def_path(did, args)?; + write!(self, " upvar_tys=")?; + args.as_coroutine().tupled_upvars_ty().print(self)?; + write!(self, " resume_ty=")?; + args.as_coroutine().resume_ty().print(self)?; + write!(self, " yield_ty=")?; + args.as_coroutine().yield_ty().print(self)?; + write!(self, " return_ty=")?; + args.as_coroutine().return_ty().print(self)?; } - p!("}}") + write!(self, "}}")? } ty::CoroutineWitness(did, args) => { - p!(write("{{")); + write!(self, "{{")?; if !self.tcx().sess.verbose_internals() { - p!("coroutine witness"); + write!(self, "coroutine witness")?; if let Some(did) = did.as_local() { let span = self.tcx().def_span(did); - p!(write( + write!( + self, "@{}", // This may end up in stderr diagnostics but it may also be emitted // into MIR. Hence we use the remapped path if available self.tcx().sess.source_map().span_to_embeddable_string(span) - )); + )?; } else { - p!(write("@"), print_def_path(did, args)); + write!(self, "@")?; + self.print_def_path(did, args)?; } } else { - p!(print_def_path(did, args)); + self.print_def_path(did, args)?; } - p!("}}") + write!(self, "}}")? } ty::Closure(did, args) => { - p!(write("{{")); + write!(self, "{{")?; if !self.should_print_verbose() { - p!(write("closure")); + write!(self, "closure")?; if self.should_truncate() { write!(self, "@...}}")?; return Ok(()); } else { if let Some(did) = did.as_local() { if self.tcx().sess.opts.unstable_opts.span_free_formats { - p!("@", print_def_path(did.to_def_id(), args)); + write!(self, "@")?; + self.print_def_path(did.to_def_id(), args)?; } else { let span = self.tcx().def_span(did); let preference = if with_forced_trimmed_paths() { @@ -958,54 +942,56 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } else { FileNameDisplayPreference::Remapped }; - p!(write( + write!( + self, "@{}", - // This may end up in stderr diagnostics but it may also be emitted - // into MIR. Hence we use the remapped path if available + // This may end up in stderr diagnostics but it may also be + // emitted into MIR. Hence we use the remapped path if + // available self.tcx().sess.source_map().span_to_string(span, preference) - )); + )?; } } else { - p!(write("@"), print_def_path(did, args)); + write!(self, "@")?; + self.print_def_path(did, args)?; } } } else { - p!(print_def_path(did, args)); - p!( - " closure_kind_ty=", - print(args.as_closure().kind_ty()), - " closure_sig_as_fn_ptr_ty=", - print(args.as_closure().sig_as_fn_ptr_ty()), - " upvar_tys=", - print(args.as_closure().tupled_upvars_ty()) - ); + self.print_def_path(did, args)?; + write!(self, " closure_kind_ty=")?; + args.as_closure().kind_ty().print(self)?; + write!(self, " closure_sig_as_fn_ptr_ty=")?; + args.as_closure().sig_as_fn_ptr_ty().print(self)?; + write!(self, " upvar_tys=")?; + args.as_closure().tupled_upvars_ty().print(self)?; } - p!("}}"); + write!(self, "}}")?; } ty::CoroutineClosure(did, args) => { - p!(write("{{")); + write!(self, "{{")?; if !self.should_print_verbose() { match self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(did)).unwrap() { hir::CoroutineKind::Desugared( hir::CoroutineDesugaring::Async, hir::CoroutineSource::Closure, - ) => p!("async closure"), + ) => write!(self, "async closure")?, hir::CoroutineKind::Desugared( hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Closure, - ) => p!("async gen closure"), + ) => write!(self, "async gen closure")?, hir::CoroutineKind::Desugared( hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure, - ) => p!("gen closure"), + ) => write!(self, "gen closure")?, _ => unreachable!( "coroutine from coroutine-closure should have CoroutineSource::Closure" ), } if let Some(did) = did.as_local() { if self.tcx().sess.opts.unstable_opts.span_free_formats { - p!("@", print_def_path(did.to_def_id(), args)); + write!(self, "@")?; + self.print_def_path(did.to_def_id(), args)?; } else { let span = self.tcx().def_span(did); let preference = if with_forced_trimmed_paths() { @@ -1013,33 +999,43 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } else { FileNameDisplayPreference::Remapped }; - p!(write( + write!( + self, "@{}", // This may end up in stderr diagnostics but it may also be emitted // into MIR. Hence we use the remapped path if available self.tcx().sess.source_map().span_to_string(span, preference) - )); + )?; } } else { - p!(write("@"), print_def_path(did, args)); + write!(self, "@")?; + self.print_def_path(did, args)?; } } else { - p!(print_def_path(did, args)); - p!( - " closure_kind_ty=", - print(args.as_coroutine_closure().kind_ty()), - " signature_parts_ty=", - print(args.as_coroutine_closure().signature_parts_ty()), - " upvar_tys=", - print(args.as_coroutine_closure().tupled_upvars_ty()), - " coroutine_captures_by_ref_ty=", - print(args.as_coroutine_closure().coroutine_captures_by_ref_ty()) - ); + self.print_def_path(did, args)?; + write!(self, " closure_kind_ty=")?; + args.as_coroutine_closure().kind_ty().print(self)?; + write!(self, " signature_parts_ty=")?; + args.as_coroutine_closure().signature_parts_ty().print(self)?; + write!(self, " upvar_tys=")?; + args.as_coroutine_closure().tupled_upvars_ty().print(self)?; + write!(self, " coroutine_captures_by_ref_ty=")?; + args.as_coroutine_closure().coroutine_captures_by_ref_ty().print(self)?; } - p!("}}"); + write!(self, "}}")?; + } + ty::Array(ty, sz) => { + write!(self, "[")?; + ty.print(self)?; + write!(self, "; ")?; + sz.print(self)?; + write!(self, "]")?; + } + ty::Slice(ty) => { + write!(self, "[")?; + ty.print(self)?; + write!(self, "]")?; } - ty::Array(ty, sz) => p!("[", print(ty), "; ", print(sz), "]"), - ty::Slice(ty) => p!("[", print(ty), "]"), } Ok(()) @@ -1137,25 +1133,25 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { 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!("("); + |(args, _), p| { + write!(p, "{}", tcx.item_name(trait_def_id))?; + write!(p, "(")?; for (idx, ty) in args.iter().enumerate() { if idx > 0 { - p!(", "); + write!(p, ", ")?; } - p!(print(ty)); + ty.print(p)?; } - p!(")"); + write!(p, ")")?; if let Some(ty) = return_ty.skip_binder().as_type() { if !ty.is_unit() { - p!(" -> ", print(return_ty)); + write!(p, " -> ")?; + return_ty.print(p)?; } } - p!(write("{}", if paren_needed { ")" } else { "" })); + write!(p, "{}", if paren_needed { ")" } else { "" })?; first = false; Ok(()) @@ -1181,13 +1177,11 @@ 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, WrapBinderMode::ForAll, |trait_pred, cx| { - define_scoped_cx!(cx); - + self.wrap_binder(&trait_pred, WrapBinderMode::ForAll, |trait_pred, p| { if trait_pred.polarity == ty::PredicatePolarity::Negative { - p!("!"); + write!(p, "!")?; } - p!(print(trait_pred.trait_ref.print_only_trait_name())); + trait_pred.trait_ref.print_only_trait_name().print(p)?; let generics = tcx.generics_of(trait_pred.def_id()); let own_args = generics.own_args_no_defaults(tcx, trait_pred.trait_ref.args); @@ -1197,32 +1191,32 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { for ty in own_args { if first { - p!("<"); + write!(p, "<")?; first = false; } else { - p!(", "); + write!(p, ", ")?; } - p!(print(ty)); + ty.print(p)?; } for (assoc_item_def_id, term) in assoc_items { if first { - p!("<"); + write!(p, "<")?; first = false; } else { - p!(", "); + write!(p, ", ")?; } - p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name())); + write!(p, "{} = ", tcx.associated_item(assoc_item_def_id).name())?; match term.skip_binder().kind() { - TermKind::Ty(ty) => p!(print(ty)), - TermKind::Const(c) => p!(print(c)), + TermKind::Ty(ty) => ty.print(p)?, + TermKind::Const(c) => c.print(p)?, }; } if !first { - p!(">"); + write!(p, ">")?; } } @@ -1321,10 +1315,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { alias_ty: ty::AliasTerm<'tcx>, ) -> Result<(), PrintError> { let def_key = self.tcx().def_key(alias_ty.def_id); - self.path_generic_args( - |cx| { - cx.path_append( - |cx| cx.path_qualified(alias_ty.self_ty(), None), + self.print_path_with_generic_args( + |p| { + p.print_path_with_simple( + |p| p.print_path_with_qualified(alias_ty.self_ty(), None), &def_key.disambiguated_data, ) }, @@ -1388,44 +1382,43 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let mut first = true; if let Some(bound_principal) = predicates.principal() { - self.wrap_binder(&bound_principal, WrapBinderMode::ForAll, |principal, cx| { - define_scoped_cx!(cx); - p!(print_def_path(principal.def_id, &[])); + self.wrap_binder(&bound_principal, WrapBinderMode::ForAll, |principal, p| { + p.print_def_path(principal.def_id, &[])?; let mut resugared = false; // Special-case `Fn(...) -> ...` and re-sugar it. - let fn_trait_kind = cx.tcx().fn_trait_kind_from_def_id(principal.def_id); - if !cx.should_print_verbose() && fn_trait_kind.is_some() { + let fn_trait_kind = p.tcx().fn_trait_kind_from_def_id(principal.def_id); + if !p.should_print_verbose() && fn_trait_kind.is_some() { if let ty::Tuple(tys) = principal.args.type_at(0).kind() { let mut projections = predicates.projection_bounds(); if let (Some(proj), None) = (projections.next(), projections.next()) { - p!(pretty_fn_sig( + p.pretty_print_fn_sig( tys, false, - proj.skip_binder().term.as_type().expect("Return type was a const") - )); + proj.skip_binder().term.as_type().expect("Return type was a const"), + )?; resugared = true; } } } - // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, + // HACK(eddyb) this duplicates `FmtPrinter`'s `print_path_with_generic_args`, // in order to place the projections inside the `<...>`. if !resugared { let principal_with_self = - principal.with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self); + principal.with_self_ty(p.tcx(), p.tcx().types.trait_object_dummy_self); - let args = cx + let args = p .tcx() .generics_of(principal_with_self.def_id) - .own_args_no_defaults(cx.tcx(), principal_with_self.args); + .own_args_no_defaults(p.tcx(), principal_with_self.args); let bound_principal_with_self = bound_principal - .with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self); + .with_self_ty(p.tcx(), p.tcx().types.trait_object_dummy_self); - let clause: ty::Clause<'tcx> = bound_principal_with_self.upcast(cx.tcx()); - let super_projections: Vec<_> = elaborate::elaborate(cx.tcx(), [clause]) + let clause: ty::Clause<'tcx> = bound_principal_with_self.upcast(p.tcx()); + let super_projections: Vec<_> = elaborate::elaborate(p.tcx(), [clause]) .filter_only_self() .filter_map(|clause| clause.as_projection_clause()) .collect(); @@ -1436,15 +1429,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // 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) + ty::ExistentialProjection::erase_self_ty(p.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 super_proj = cx.tcx().erase_regions(super_proj); + let proj = p.tcx().erase_regions(proj); + let super_proj = p.tcx().erase_regions(super_proj); proj == super_proj }); @@ -1458,16 +1451,16 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { .collect(); projections - .sort_by_cached_key(|proj| cx.tcx().item_name(proj.def_id).to_string()); + .sort_by_cached_key(|proj| p.tcx().item_name(proj.def_id).to_string()); if !args.is_empty() || !projections.is_empty() { - p!(generic_delimiters(|cx| { - cx.comma_sep(args.iter().copied())?; + p.generic_delimiters(|p| { + p.comma_sep(args.iter().copied())?; if !args.is_empty() && !projections.is_empty() { - write!(cx, ", ")?; + write!(p, ", ")?; } - cx.comma_sep(projections.iter().copied()) - })); + p.comma_sep(projections.iter().copied()) + })?; } } Ok(()) @@ -1476,11 +1469,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { first = false; } - define_scoped_cx!(self); - // Builtin bounds. // FIXME(eddyb) avoid printing twice (needed to ensure - // that the auto traits are sorted *and* printed via cx). + // that the auto traits are sorted *and* printed via p). let mut auto_traits: Vec<_> = predicates.auto_traits().collect(); // The auto traits come ordered by `DefPathHash`. While @@ -1494,34 +1485,34 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { for def_id in auto_traits { if !first { - p!(" + "); + write!(self, " + ")?; } first = false; - p!(print_def_path(def_id, &[])); + self.print_def_path(def_id, &[])?; } Ok(()) } - fn pretty_fn_sig( + fn pretty_print_fn_sig( &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, output: Ty<'tcx>, ) -> Result<(), PrintError> { - define_scoped_cx!(self); - - p!("(", comma_sep(inputs.iter().copied())); + write!(self, "(")?; + self.comma_sep(inputs.iter().copied())?; if c_variadic { if !inputs.is_empty() { - p!(", "); + write!(self, ", ")?; } - p!("..."); + write!(self, "...")?; } - p!(")"); + write!(self, ")")?; if !output.is_unit() { - p!(" -> ", print(output)); + write!(self, " -> ")?; + output.print(self)?; } Ok(()) @@ -1532,10 +1523,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ct: ty::Const<'tcx>, print_ty: bool, ) -> Result<(), PrintError> { - define_scoped_cx!(self); - if self.should_print_verbose() { - p!(write("{:?}", ct)); + write!(self, "{ct:?}")?; return Ok(()); } @@ -1543,25 +1532,28 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { match self.tcx().def_kind(def) { DefKind::Const | DefKind::AssocConst => { - p!(print_value_path(def, args)) + self.pretty_print_value_path(def, args)?; } DefKind::AnonConst => { if def.is_local() && let span = self.tcx().def_span(def) && let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) { - p!(write("{}", snip)) + write!(self, "{snip}")?; } else { - // Do not call `print_value_path` as if a parent of this anon const is an impl it will - // attempt to print out the impl trait ref i.e. `<T as Trait>::{constant#0}`. This would - // cause printing to enter an infinite recursion if the anon const is in the self type i.e. - // `impl<T: Default> Default for [T; 32 - 1 - 1 - 1] {` - // where we would try to print `<[T; /* print `constant#0` again */] as Default>::{constant#0}` - p!(write( + // Do not call `pretty_print_value_path` as if a parent of this anon + // const is an impl it will attempt to print out the impl trait ref + // i.e. `<T as Trait>::{constant#0}`. This would cause printing to + // enter an infinite recursion if the anon const is in the self type + // i.e. `impl<T: Default> Default for [T; 32 - 1 - 1 - 1] {` where we + // would try to print `<[T; /* print constant#0 again */] as // + // Default>::{constant#0}`. + write!( + self, "{}::{}", self.tcx().crate_name(def.krate), self.tcx().def_path(def).to_string_no_crate_verbose() - )) + )?; } } defkind => bug!("`{:?}` has unexpected defkind {:?}", ct, defkind), @@ -1569,11 +1561,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::ConstKind::Infer(infer_ct) => match infer_ct { ty::InferConst::Var(ct_vid) if let Some(name) = self.const_infer_name(ct_vid) => { - p!(write("{}", name)) + write!(self, "{name}")?; } _ => write!(self, "_")?, }, - ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), + ty::ConstKind::Param(ParamConst { name, .. }) => write!(self, "{name}")?, ty::ConstKind::Value(cv) => { return self.pretty_print_const_valtree(cv, print_ty); } @@ -1581,11 +1573,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Bound(debruijn, bound_var) => { rustc_type_ir::debug_bound_var(self, debruijn, bound_var)? } - ty::ConstKind::Placeholder(placeholder) => p!(write("{placeholder:?}")), + ty::ConstKind::Placeholder(placeholder) => write!(self, "{placeholder:?}")?, // FIXME(generic_const_exprs): // write out some legible representation of an abstract const? ty::ConstKind::Expr(expr) => self.pretty_print_const_expr(expr, print_ty)?, - ty::ConstKind::Error(_) => p!("{{const error}}"), + ty::ConstKind::Error(_) => write!(self, "{{const error}}")?, }; Ok(()) } @@ -1595,7 +1587,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { expr: Expr<'tcx>, print_ty: bool, ) -> Result<(), PrintError> { - define_scoped_cx!(self); match expr.kind { ty::ExprKind::Binop(op) => { let (_, _, c1, c2) = expr.binop_args(); @@ -1634,7 +1625,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { |this| this.pretty_print_const(c1, print_ty), lhs_parenthesized, )?; - p!(write(" {formatted_op} ")); + write!(self, " {formatted_op} ")?; self.maybe_parenthesized( |this| this.pretty_print_const(c2, print_ty), rhs_parenthesized, @@ -1657,7 +1648,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Expr(_) => true, _ => false, }; - p!(write("{formatted_op}")); + write!(self, "{formatted_op}")?; self.maybe_parenthesized( |this| this.pretty_print_const(ct, print_ty), parenthesized, @@ -1668,7 +1659,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { write!(self, "(")?; self.pretty_print_const(fn_def, print_ty)?; - p!(")(", comma_sep(fn_args), ")"); + write!(self, ")(")?; + self.comma_sep(fn_args)?; + write!(self, ")")?; } ty::ExprKind::Cast(kind) => { let (_, value, to_ty) = expr.cast_args(); @@ -1718,8 +1711,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ptr: Pointer, ty: Ty<'tcx>, ) -> Result<(), PrintError> { - define_scoped_cx!(self); - let (prov, offset) = ptr.prov_and_relative_offset(); match ty.kind() { // Byte strings (&[u8; N]) @@ -1734,19 +1725,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if let Ok(byte_str) = alloc.inner().get_bytes_strip_provenance(&self.tcx(), range) { - p!(pretty_print_byte_str(byte_str)) + self.pretty_print_byte_str(byte_str)?; } else { - p!("<too short allocation>") + write!(self, "<too short allocation>")?; } } // FIXME: for statics, vtables, and functions, we could in principle print more detail. Some(GlobalAlloc::Static(def_id)) => { - p!(write("<static({:?})>", def_id)) + write!(self, "<static({def_id:?})>")?; } - Some(GlobalAlloc::Function { .. }) => p!("<function>"), - Some(GlobalAlloc::VTable(..)) => p!("<vtable>"), - Some(GlobalAlloc::TypeId { .. }) => p!("<typeid>"), - None => p!("<dangling pointer>"), + Some(GlobalAlloc::Function { .. }) => write!(self, "<function>")?, + Some(GlobalAlloc::VTable(..)) => write!(self, "<vtable>")?, + Some(GlobalAlloc::TypeId { .. }) => write!(self, "<typeid>")?, + None => write!(self, "<dangling pointer>")?, } return Ok(()); } @@ -1758,7 +1749,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.tcx().try_get_global_alloc(prov.alloc_id()) { self.typed_value( - |this| this.print_value_path(instance.def_id(), instance.args), + |this| this.pretty_print_value_path(instance.def_id(), instance.args), |this| this.print_type(ty), " as ", )?; @@ -1778,40 +1769,38 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty: Ty<'tcx>, print_ty: bool, ) -> Result<(), PrintError> { - define_scoped_cx!(self); - match ty.kind() { // Bool - ty::Bool if int == ScalarInt::FALSE => p!("false"), - ty::Bool if int == ScalarInt::TRUE => p!("true"), + ty::Bool if int == ScalarInt::FALSE => write!(self, "false")?, + ty::Bool if int == ScalarInt::TRUE => write!(self, "true")?, // Float ty::Float(fty) => match fty { ty::FloatTy::F16 => { let val = Half::try_from(int).unwrap(); - p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" })) + write!(self, "{}{}f16", val, if val.is_finite() { "" } else { "_" })?; } ty::FloatTy::F32 => { let val = Single::try_from(int).unwrap(); - p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" })) + write!(self, "{}{}f32", val, if val.is_finite() { "" } else { "_" })?; } ty::FloatTy::F64 => { let val = Double::try_from(int).unwrap(); - p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" })) + write!(self, "{}{}f64", val, if val.is_finite() { "" } else { "_" })?; } ty::FloatTy::F128 => { let val = Quad::try_from(int).unwrap(); - p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" })) + write!(self, "{}{}f128", val, if val.is_finite() { "" } else { "_" })?; } }, // Int ty::Uint(_) | ty::Int(_) => { let int = ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral()); - if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } + if print_ty { write!(self, "{int:#?}")? } else { write!(self, "{int:?}")? } } // Char ty::Char if char::try_from(int).is_ok() => { - p!(write("{:?}", char::try_from(int).unwrap())) + write!(self, "{:?}", char::try_from(int).unwrap())?; } // Pointer types ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(..) => { @@ -1827,7 +1816,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::Pat(base_ty, pat) if self.tcx().validate_scalar_in_layout(int, ty) => { self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?; - p!(write(" is {pat:?}")); + write!(self, " is {pat:?}")?; } // Nontrivial types with scalar bit representation _ => { @@ -1876,10 +1865,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { cv: ty::Value<'tcx>, print_ty: bool, ) -> Result<(), PrintError> { - define_scoped_cx!(self); - if with_reduced_queries() || self.should_print_verbose() { - p!(write("ValTree({:?}: ", cv.valtree), print(cv.ty), ")"); + write!(self, "ValTree({:?}: ", cv.valtree)?; + cv.ty.print(self)?; + write!(self, ")")?; return Ok(()); } @@ -1900,13 +1889,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", cv.ty) }); - p!(write("{:?}", String::from_utf8_lossy(bytes))); + write!(self, "{:?}", String::from_utf8_lossy(bytes))?; return Ok(()); } _ => { let cv = ty::Value { valtree: cv.valtree, ty: inner_ty }; - p!("&"); - p!(pretty_print_const_valtree(cv, print_ty)); + write!(self, "&")?; + self.pretty_print_const_valtree(cv, print_ty)?; return Ok(()); } }, @@ -1914,8 +1903,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); - p!("*"); - p!(pretty_print_byte_str(bytes)); + write!(self, "*")?; + self.pretty_print_byte_str(bytes)?; return Ok(()); } // Aggregates, printed as array/tuple/struct/variant construction syntax. @@ -1928,14 +1917,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let fields = contents.fields.iter().copied(); match *cv.ty.kind() { ty::Array(..) => { - p!("[", comma_sep(fields), "]"); + write!(self, "[")?; + self.comma_sep(fields)?; + write!(self, "]")?; } ty::Tuple(..) => { - p!("(", comma_sep(fields)); + write!(self, "(")?; + self.comma_sep(fields)?; if contents.fields.len() == 1 { - p!(","); + write!(self, ",")?; } - p!(")"); + write!(self, ")")?; } ty::Adt(def, _) if def.variants().is_empty() => { self.typed_value( @@ -1951,23 +1943,26 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let variant_idx = contents.variant.expect("destructed const of adt without variant idx"); let variant_def = &def.variant(variant_idx); - p!(print_value_path(variant_def.def_id, args)); + self.pretty_print_value_path(variant_def.def_id, args)?; match variant_def.ctor_kind() { Some(CtorKind::Const) => {} Some(CtorKind::Fn) => { - p!("(", comma_sep(fields), ")"); + write!(self, "(")?; + self.comma_sep(fields)?; + write!(self, ")")?; } None => { - p!(" {{ "); + write!(self, " {{ ")?; let mut first = true; for (field_def, field) in iter::zip(&variant_def.fields, fields) { if !first { - p!(", "); + write!(self, ", ")?; } - p!(write("{}: ", field_def.name), print(field)); + write!(self, "{}: ", field_def.name)?; + field.print(self)?; first = false; } - p!(" }}"); + write!(self, " }}")?; } } } @@ -1976,7 +1971,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(()); } (ty::ValTreeKind::Leaf(leaf), ty::Ref(_, inner_ty, _)) => { - p!(write("&")); + write!(self, "&")?; return self.pretty_print_const_scalar_int(*leaf, inner_ty, print_ty); } (ty::ValTreeKind::Leaf(leaf), _) => { @@ -1984,7 +1979,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } (_, ty::FnDef(def_id, args)) => { // Never allowed today, but we still encounter them in invalid const args. - p!(print_value_path(def_id, args)); + self.pretty_print_value_path(def_id, args)?; return Ok(()); } // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading @@ -1994,17 +1989,18 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // fallback if cv.valtree.is_zst() { - p!(write("<ZST>")); + write!(self, "<ZST>")?; } else { - p!(write("{:?}", cv.valtree)); + write!(self, "{:?}", cv.valtree)?; } if print_ty { - p!(": ", print(cv.ty)); + write!(self, ": ")?; + cv.ty.print(self)?; } Ok(()) } - fn pretty_closure_as_impl( + fn pretty_print_closure_as_impl( &mut self, closure: ty::ClosureArgs<TyCtxt<'tcx>>, ) -> Result<(), PrintError> { @@ -2012,20 +2008,19 @@ 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, WrapBinderMode::ForAll, |sig, cx| { - define_scoped_cx!(cx); - - p!(write("{kind}(")); + self.wrap_binder(&sig, WrapBinderMode::ForAll, |sig, p| { + write!(p, "{kind}(")?; for (i, arg) in sig.inputs()[0].tuple_fields().iter().enumerate() { if i > 0 { - p!(", "); + write!(p, ", ")?; } - p!(print(arg)); + arg.print(p)?; } - p!(")"); + write!(p, ")")?; if !sig.output().is_unit() { - p!(" -> ", print(sig.output())); + write!(p, " -> ")?; + sig.output().print(p)?; } Ok(()) @@ -2036,15 +2031,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { &mut self, constness: ty::BoundConstness, ) -> Result<(), PrintError> { - define_scoped_cx!(self); - match constness { - ty::BoundConstness::Const => { - p!("const "); - } - ty::BoundConstness::Maybe => { - p!("[const] "); - } + ty::BoundConstness::Const => write!(self, "const ")?, + ty::BoundConstness::Maybe => write!(self, "[const] ")?, } Ok(()) } @@ -2061,10 +2050,10 @@ pub(crate) fn pretty_print_const<'tcx>( ) -> fmt::Result { ty::tls::with(|tcx| { let literal = tcx.lift(c).unwrap(); - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - cx.pretty_print_const(literal, print_types)?; - fmt.write_str(&cx.into_buffer())?; + let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); + p.print_alloc_ids = true; + p.pretty_print_const(literal, print_types)?; + fmt.write_str(&p.into_buffer())?; Ok(()) }) } @@ -2149,8 +2138,6 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { } } -// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always -// (but also some things just print a `DefId` generally so maybe we need this?) fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { match tcx.def_key(def_id).disambiguated_data.data { DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::OpaqueTy => { @@ -2175,6 +2162,7 @@ impl<'t> TyCtxt<'t> { self.def_path_str_with_args(def_id, &[]) } + /// For this one we determine the appropriate namespace for the `def_id`. pub fn def_path_str_with_args( self, def_id: impl IntoQueryParam<DefId>, @@ -2184,19 +2172,20 @@ impl<'t> TyCtxt<'t> { let ns = guess_def_namespace(self, def_id); debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns); - FmtPrinter::print_string(self, ns, |cx| cx.print_def_path(def_id, args)).unwrap() + FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap() } + /// For this one we always use value namespace. pub fn value_path_str_with_args( self, def_id: impl IntoQueryParam<DefId>, args: &'t [GenericArg<'t>], ) -> String { let def_id = def_id.into_query_param(); - let ns = guess_def_namespace(self, def_id); + let ns = Namespace::ValueNS; debug!("value_path_str: def_id={:?}, ns={:?}", def_id, ns); - FmtPrinter::print_string(self, ns, |cx| cx.print_value_path(def_id, args)).unwrap() + FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap() } } @@ -2248,7 +2237,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self.print_def_path(parent_def_id, &[])?; - // HACK(eddyb) copy of `path_append` to avoid + // HACK(eddyb) copy of `print_path_with_simple` to avoid // constructing a `DisambiguatedDefPathData`. if !self.empty_path { write!(self, "::")?; @@ -2313,10 +2302,6 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } } - fn should_truncate(&mut self) -> bool { - !self.type_length_limit.value_within_limit(self.printed_type_count) - } - fn print_dyn_existential( &mut self, predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, @@ -2328,7 +2313,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self.pretty_print_const(ct, false) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; if cnum == LOCAL_CRATE { if self.tcx.sess.at_least_rust_2018() { @@ -2345,28 +2330,27 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<(), PrintError> { - self.pretty_path_qualified(self_ty, trait_ref)?; + self.pretty_print_path_with_qualified(self_ty, trait_ref)?; self.empty_path = false; Ok(()) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - _disambiguated_data: &DisambiguatedDefPathData, self_ty: Ty<'tcx>, trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( - |cx| { - print_prefix(cx)?; - if !cx.empty_path { - write!(cx, "::")?; + self.pretty_print_path_with_impl( + |p| { + print_prefix(p)?; + if !p.empty_path { + write!(p, "::")?; } Ok(()) @@ -2378,7 +2362,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -2409,7 +2393,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], @@ -2420,7 +2404,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { if self.in_value { write!(self, "::")?; } - self.generic_delimiters(|cx| cx.comma_sep(args.iter().copied())) + self.generic_delimiters(|p| p.comma_sep(args.iter().copied())) } else { Ok(()) } @@ -2440,7 +2424,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } - fn print_value_path( + fn pretty_print_value_path( &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], @@ -2452,11 +2436,11 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn pretty_print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>, { - self.pretty_print_in_binder(value) + self.wrap_binder(value, WrapBinderMode::ForAll, |new_value, this| new_value.print(this)) } fn wrap_binder<T, C: FnOnce(&T, &mut Self) -> Result<(), PrintError>>( @@ -2468,7 +2452,12 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - self.pretty_wrap_binder(value, mode, f) + let old_region_index = self.region_index; + let (new_value, _) = self.name_all_regions(value, mode)?; + f(&new_value, self)?; + self.region_index = old_region_index; + self.binder_depth -= 1; + Ok(()) } fn typed_value( @@ -2501,7 +2490,11 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn should_print_region(&self, region: ty::Region<'tcx>) -> bool { + fn should_truncate(&mut self) -> bool { + !self.type_length_limit.value_within_limit(self.printed_type_count) + } + + fn should_print_optional_region(&self, region: ty::Region<'tcx>) -> bool { let highlight = self.region_highlight_mode; if highlight.region_highlighted(region).is_some() { return true; @@ -2552,11 +2545,10 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { ty: Ty<'tcx>, ) -> Result<(), PrintError> { let print = |this: &mut Self| { - define_scoped_cx!(this); if this.print_alloc_ids { - p!(write("{:?}", p)); + write!(this, "{p:?}")?; } else { - p!("&_"); + write!(this, "&_")?; } Ok(()) }; @@ -2567,17 +2559,15 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { // HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`. impl<'tcx> FmtPrinter<'_, 'tcx> { pub fn pretty_print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), fmt::Error> { - define_scoped_cx!(self); - // Watch out for region highlights. let highlight = self.region_highlight_mode; if let Some(n) = highlight.region_highlighted(region) { - p!(write("'{}", n)); + write!(self, "'{n}")?; return Ok(()); } if self.should_print_verbose() { - p!(write("{:?}", region)); + write!(self, "{region:?}")?; return Ok(()); } @@ -2589,12 +2579,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // `explain_region()` or `note_and_explain_region()`. match region.kind() { ty::ReEarlyParam(data) => { - p!(write("{}", data.name)); + write!(self, "{}", data.name)?; return Ok(()); } ty::ReLateParam(ty::LateParamRegion { kind, .. }) => { if let Some(name) = kind.get_name(self.tcx) { - p!(write("{}", name)); + write!(self, "{name}")?; return Ok(()); } } @@ -2603,31 +2593,31 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { bound: ty::BoundRegion { kind: br, .. }, .. }) => { if let Some(name) = br.get_name(self.tcx) { - p!(write("{}", name)); + write!(self, "{name}")?; return Ok(()); } if let Some((region, counter)) = highlight.highlight_bound_region { if br == region { - p!(write("'{}", counter)); + write!(self, "'{counter}")?; return Ok(()); } } } ty::ReVar(region_vid) if identify_regions => { - p!(write("{:?}", region_vid)); + write!(self, "{region_vid:?}")?; return Ok(()); } ty::ReVar(_) => {} ty::ReErased => {} ty::ReError(_) => {} ty::ReStatic => { - p!("'static"); + write!(self, "'static")?; return Ok(()); } } - p!("'_"); + write!(self, "'_")?; Ok(()) } @@ -2745,17 +2735,17 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { debug!("self.used_region_names: {:?}", self.used_region_names); let mut empty = true; - let mut start_or_continue = |cx: &mut Self, start: &str, cont: &str| { + let mut start_or_continue = |p: &mut Self, start: &str, cont: &str| { let w = if empty { empty = false; start } else { cont }; - let _ = write!(cx, "{w}"); + let _ = write!(p, "{w}"); }; - let do_continue = |cx: &mut Self, cont: Symbol| { - let _ = write!(cx, "{cont}"); + let do_continue = |p: &mut Self, cont: Symbol| { + let _ = write!(p, "{cont}"); }; let possible_names = ('a'..='z').rev().map(|s| Symbol::intern(&format!("'{s}"))); @@ -2855,38 +2845,6 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { Ok((new_value, map)) } - 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, WrapBinderMode::ForAll)?; - new_value.print(self)?; - self.region_index = old_region_index; - self.binder_depth -= 1; - Ok(()) - } - - 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, mode)?; - f(&new_value, self)?; - self.region_index = old_region_index; - self.binder_depth -= 1; - Ok(()) - } - fn prepare_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>) where T: TypeFoldable<TyCtxt<'tcx>>, @@ -2940,8 +2898,8 @@ impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder<'tcx, T> where T: Print<'tcx, P> + TypeFoldable<TyCtxt<'tcx>>, { - fn print(&self, cx: &mut P) -> Result<(), PrintError> { - cx.print_in_binder(self) + fn print(&self, p: &mut P) -> Result<(), PrintError> { + p.pretty_print_in_binder(self) } } @@ -2949,9 +2907,10 @@ impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate<' where T: Print<'tcx, P>, { - fn print(&self, cx: &mut P) -> Result<(), PrintError> { - define_scoped_cx!(cx); - p!(print(self.0), ": ", print(self.1)); + fn print(&self, p: &mut P) -> Result<(), PrintError> { + self.0.print(p)?; + write!(p, ": ")?; + self.1.print(p)?; Ok(()) } } @@ -3035,7 +2994,7 @@ impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> { } } -#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift, Hash)] pub struct TraitPredPrintModifiersAndPath<'tcx>(ty::TraitPredicate<'tcx>); impl<'tcx> fmt::Debug for TraitPredPrintModifiersAndPath<'tcx> { @@ -3090,11 +3049,11 @@ macro_rules! forward_display_to_print { $(#[allow(unused_lifetimes)] impl<'tcx> fmt::Display for $ty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - let mut cx = FmtPrinter::new(tcx, Namespace::TypeNS); + let mut p = FmtPrinter::new(tcx, Namespace::TypeNS); tcx.lift(*self) .expect("could not lift for printing") - .print(&mut cx)?; - f.write_str(&cx.into_buffer())?; + .print(&mut p)?; + f.write_str(&p.into_buffer())?; Ok(()) }) } @@ -3103,10 +3062,9 @@ macro_rules! forward_display_to_print { } macro_rules! define_print { - (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { + (($self:ident, $p:ident): $($ty:ty $print:block)+) => { $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { - fn print(&$self, $cx: &mut P) -> Result<(), PrintError> { - define_scoped_cx!($cx); + fn print(&$self, $p: &mut P) -> Result<(), PrintError> { let _: () = $print; Ok(()) } @@ -3115,8 +3073,8 @@ macro_rules! define_print { } macro_rules! define_print_and_forward_display { - (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { - define_print!(($self, $cx): $($ty $print)*); + (($self:ident, $p:ident): $($ty:ty $print:block)+) => { + define_print!(($self, $p): $($ty $print)*); forward_display_to_print!($($ty),+); }; } @@ -3129,37 +3087,38 @@ forward_display_to_print! { } define_print! { - (self, cx): + (self, p): ty::FnSig<'tcx> { - p!(write("{}", self.safety.prefix_str())); + write!(p, "{}", self.safety.prefix_str())?; if self.abi != ExternAbi::Rust { - p!(write("extern {} ", self.abi)); + write!(p, "extern {} ", self.abi)?; } - p!("fn", pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); + write!(p, "fn")?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic, self.output())?; } ty::TraitRef<'tcx> { - p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path())) + write!(p, "<{} as {}>", self.self_ty(), self.print_only_trait_path())?; } ty::AliasTy<'tcx> { let alias_term: ty::AliasTerm<'tcx> = (*self).into(); - p!(print(alias_term)) + alias_term.print(p)?; } ty::AliasTerm<'tcx> { - match self.kind(cx.tcx()) { - ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => p!(pretty_print_inherent_projection(*self)), + match self.kind(p.tcx()) { + ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => p.pretty_print_inherent_projection(*self)?, ty::AliasTermKind::ProjectionTy => { - if !(cx.should_print_verbose() || with_reduced_queries()) - && cx.tcx().is_impl_trait_in_trait(self.def_id) + if !(p.should_print_verbose() || with_reduced_queries()) + && p.tcx().is_impl_trait_in_trait(self.def_id) { - p!(pretty_print_rpitit(self.def_id, self.args)) + p.pretty_print_rpitit(self.def_id, self.args)?; } else { - p!(print_def_path(self.def_id, self.args)); + p.print_def_path(self.def_id, self.args)?; } } ty::AliasTermKind::FreeTy @@ -3167,17 +3126,18 @@ define_print! { | ty::AliasTermKind::OpaqueTy | ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => { - p!(print_def_path(self.def_id, self.args)); + p.print_def_path(self.def_id, self.args)?; } } } ty::TraitPredicate<'tcx> { - p!(print(self.trait_ref.self_ty()), ": "); + self.trait_ref.self_ty().print(p)?; + write!(p, ": ")?; if let ty::PredicatePolarity::Negative = self.polarity { - p!("!"); + write!(p, "!")?; } - p!(print(self.trait_ref.print_trait_sugared())) + self.trait_ref.print_trait_sugared().print(p)?; } ty::HostEffectPredicate<'tcx> { @@ -3185,196 +3145,223 @@ define_print! { ty::BoundConstness::Const => { "const" } ty::BoundConstness::Maybe => { "[const]" } }; - p!(print(self.trait_ref.self_ty()), ": {constness} "); - p!(print(self.trait_ref.print_trait_sugared())) + self.trait_ref.self_ty().print(p)?; + write!(p, ": {constness} ")?; + self.trait_ref.print_trait_sugared().print(p)?; } ty::TypeAndMut<'tcx> { - p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) + write!(p, "{}", self.mutbl.prefix_str())?; + self.ty.print(p)?; } ty::ClauseKind<'tcx> { match *self { - ty::ClauseKind::Trait(ref data) => { - p!(print(data)) - } - ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)), - ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)), - ty::ClauseKind::Projection(predicate) => p!(print(predicate)), - ty::ClauseKind::HostEffect(predicate) => p!(print(predicate)), + ty::ClauseKind::Trait(ref data) => data.print(p)?, + ty::ClauseKind::RegionOutlives(predicate) => predicate.print(p)?, + ty::ClauseKind::TypeOutlives(predicate) => predicate.print(p)?, + ty::ClauseKind::Projection(predicate) => predicate.print(p)?, + ty::ClauseKind::HostEffect(predicate) => predicate.print(p)?, ty::ClauseKind::ConstArgHasType(ct, ty) => { - p!("the constant `", print(ct), "` has type `", print(ty), "`") + write!(p, "the constant `")?; + ct.print(p)?; + write!(p, "` has type `")?; + ty.print(p)?; + write!(p, "`")?; }, - ty::ClauseKind::WellFormed(term) => p!(print(term), " well-formed"), + ty::ClauseKind::WellFormed(term) => { + term.print(p)?; + write!(p, " well-formed")?; + } ty::ClauseKind::ConstEvaluatable(ct) => { - p!("the constant `", print(ct), "` can be evaluated") + write!(p, "the constant `")?; + ct.print(p)?; + write!(p, "` can be evaluated")?; + } + ty::ClauseKind::UnstableFeature(symbol) => { + write!(p, "unstable feature: ")?; + write!(p, "`{symbol}`")?; } - ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)), } } ty::PredicateKind<'tcx> { match *self { - ty::PredicateKind::Clause(data) => { - p!(print(data)) - } - ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), - ty::PredicateKind::Coerce(predicate) => p!(print(predicate)), + ty::PredicateKind::Clause(data) => data.print(p)?, + ty::PredicateKind::Subtype(predicate) => predicate.print(p)?, + ty::PredicateKind::Coerce(predicate) => predicate.print(p)?, ty::PredicateKind::DynCompatible(trait_def_id) => { - p!("the trait `", print_def_path(trait_def_id, &[]), "` is dyn-compatible") + write!(p, "the trait `")?; + p.print_def_path(trait_def_id, &[])?; + write!(p, "` is dyn-compatible")?; } ty::PredicateKind::ConstEquate(c1, c2) => { - p!("the constant `", print(c1), "` equals `", print(c2), "`") + write!(p, "the constant `")?; + c1.print(p)?; + write!(p, "` equals `")?; + c2.print(p)?; + write!(p, "`")?; + } + ty::PredicateKind::Ambiguous => write!(p, "ambiguous")?, + ty::PredicateKind::NormalizesTo(data) => data.print(p)?, + ty::PredicateKind::AliasRelate(t1, t2, dir) => { + t1.print(p)?; + write!(p, " {dir} ")?; + t2.print(p)?; } - ty::PredicateKind::Ambiguous => p!("ambiguous"), - ty::PredicateKind::NormalizesTo(data) => p!(print(data)), - ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), } } ty::ExistentialPredicate<'tcx> { match *self { - ty::ExistentialPredicate::Trait(x) => p!(print(x)), - ty::ExistentialPredicate::Projection(x) => p!(print(x)), - ty::ExistentialPredicate::AutoTrait(def_id) => { - p!(print_def_path(def_id, &[])); - } + ty::ExistentialPredicate::Trait(x) => x.print(p)?, + ty::ExistentialPredicate::Projection(x) => x.print(p)?, + ty::ExistentialPredicate::AutoTrait(def_id) => p.print_def_path(def_id, &[])?, } } ty::ExistentialTraitRef<'tcx> { // Use a type that can't appear in defaults of type parameters. - let dummy_self = Ty::new_fresh(cx.tcx(), 0); - let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); - p!(print(trait_ref.print_only_trait_path())) + let dummy_self = Ty::new_fresh(p.tcx(), 0); + let trait_ref = self.with_self_ty(p.tcx(), dummy_self); + trait_ref.print_only_trait_path().print(p)?; } ty::ExistentialProjection<'tcx> { - let name = cx.tcx().associated_item(self.def_id).name(); + let name = p.tcx().associated_item(self.def_id).name(); // The args don't contain the self ty (as it has been erased) but the corresp. // generics do as the trait always has a self ty param. We need to offset. - let args = &self.args[cx.tcx().generics_of(self.def_id).parent_count - 1..]; - p!(path_generic_args(|cx| write!(cx, "{name}"), args), " = ", print(self.term)) + let args = &self.args[p.tcx().generics_of(self.def_id).parent_count - 1..]; + p.print_path_with_generic_args(|p| write!(p, "{name}"), args)?; + write!(p, " = ")?; + self.term.print(p)?; } ty::ProjectionPredicate<'tcx> { - p!(print(self.projection_term), " == "); - cx.reset_type_limit(); - p!(print(self.term)) + self.projection_term.print(p)?; + write!(p, " == ")?; + p.reset_type_limit(); + self.term.print(p)?; } ty::SubtypePredicate<'tcx> { - p!(print(self.a), " <: "); - cx.reset_type_limit(); - p!(print(self.b)) + self.a.print(p)?; + write!(p, " <: ")?; + p.reset_type_limit(); + self.b.print(p)?; } ty::CoercePredicate<'tcx> { - p!(print(self.a), " -> "); - cx.reset_type_limit(); - p!(print(self.b)) + self.a.print(p)?; + write!(p, " -> ")?; + p.reset_type_limit(); + self.b.print(p)?; } ty::NormalizesTo<'tcx> { - p!(print(self.alias), " normalizes-to "); - cx.reset_type_limit(); - p!(print(self.term)) + self.alias.print(p)?; + write!(p, " normalizes-to ")?; + p.reset_type_limit(); + self.term.print(p)?; } } define_print_and_forward_display! { - (self, cx): + (self, p): &'tcx ty::List<Ty<'tcx>> { - p!("{{", comma_sep(self.iter()), "}}") + write!(p, "{{")?; + p.comma_sep(self.iter())?; + write!(p, "}}")?; } TraitRefPrintOnlyTraitPath<'tcx> { - p!(print_def_path(self.0.def_id, self.0.args)); + p.print_def_path(self.0.def_id, self.0.args)?; } TraitRefPrintSugared<'tcx> { if !with_reduced_queries() - && cx.tcx().trait_def(self.0.def_id).paren_sugar + && p.tcx().trait_def(self.0.def_id).paren_sugar && let ty::Tuple(args) = self.0.args.type_at(1).kind() { - p!(write("{}", cx.tcx().item_name(self.0.def_id)), "("); + write!(p, "{}(", p.tcx().item_name(self.0.def_id))?; for (i, arg) in args.iter().enumerate() { if i > 0 { - p!(", "); + write!(p, ", ")?; } - p!(print(arg)); + arg.print(p)?; } - p!(")"); + write!(p, ")")?; } else { - p!(print_def_path(self.0.def_id, self.0.args)); + p.print_def_path(self.0.def_id, self.0.args)?; } } TraitRefPrintOnlyTraitName<'tcx> { - p!(print_def_path(self.0.def_id, &[])); + p.print_def_path(self.0.def_id, &[])?; } TraitPredPrintModifiersAndPath<'tcx> { if let ty::PredicatePolarity::Negative = self.0.polarity { - p!("!") + write!(p, "!")?; } - p!(print(self.0.trait_ref.print_trait_sugared())); + self.0.trait_ref.print_trait_sugared().print(p)?; } TraitPredPrintWithBoundConstness<'tcx> { - p!(print(self.0.trait_ref.self_ty()), ": "); + self.0.trait_ref.self_ty().print(p)?; + write!(p, ": ")?; if let Some(constness) = self.1 { - p!(pretty_print_bound_constness(constness)); + p.pretty_print_bound_constness(constness)?; } if let ty::PredicatePolarity::Negative = self.0.polarity { - p!("!"); + write!(p, "!")?; } - p!(print(self.0.trait_ref.print_trait_sugared())) + self.0.trait_ref.print_trait_sugared().print(p)?; } PrintClosureAsImpl<'tcx> { - p!(pretty_closure_as_impl(self.closure)) + p.pretty_print_closure_as_impl(self.closure)?; } ty::ParamTy { - p!(write("{}", self.name)) + write!(p, "{}", self.name)?; } ty::PlaceholderType { match self.bound.kind { - ty::BoundTyKind::Anon => p!(write("{self:?}")), - ty::BoundTyKind::Param(def_id) => match cx.should_print_verbose() { - true => p!(write("{self:?}")), - false => p!(write("{}", cx.tcx().item_name(def_id))), + ty::BoundTyKind::Anon => write!(p, "{self:?}")?, + ty::BoundTyKind::Param(def_id) => match p.should_print_verbose() { + true => write!(p, "{self:?}")?, + false => write!(p, "{}", p.tcx().item_name(def_id))?, }, } } ty::ParamConst { - p!(write("{}", self.name)) + write!(p, "{}", self.name)?; } ty::Term<'tcx> { match self.kind() { - ty::TermKind::Ty(ty) => p!(print(ty)), - ty::TermKind::Const(c) => p!(print(c)), + ty::TermKind::Ty(ty) => ty.print(p)?, + ty::TermKind::Const(c) => c.print(p)?, } } ty::Predicate<'tcx> { - p!(print(self.kind())) + self.kind().print(p)?; } ty::Clause<'tcx> { - p!(print(self.kind())) + self.kind().print(p)?; } GenericArg<'tcx> { match self.kind() { - GenericArgKind::Lifetime(lt) => p!(print(lt)), - GenericArgKind::Type(ty) => p!(print(ty)), - GenericArgKind::Const(ct) => p!(print(ct)), + GenericArgKind::Lifetime(lt) => lt.print(p)?, + GenericArgKind::Type(ty) => ty.print(p)?, + GenericArgKind::Const(ct) => ct.print(p)?, } } } diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 9bf6e3a7590..7dfe2d28051 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -44,7 +44,7 @@ impl RvalueScopes { debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); return (Some(id), backwards_incompatible); } - ScopeData::IfThenRescope => { + ScopeData::IfThenRescope | ScopeData::MatchGuard => { debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]"); return (Some(p), backwards_incompatible); } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 10e499d9c75..89ef46b1ae5 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -12,7 +12,6 @@ use rustc_hir::def_id::LocalDefId; use rustc_span::source_map::Spanned; use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit}; -use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Pattern, Region}; use crate::mir::PlaceElem; use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths}; @@ -25,8 +24,8 @@ impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { with_no_trimmed_paths!({ - let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |cx| { - cx.print_def_path(self.def_id, &[]) + let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |p| { + p.print_def_path(self.def_id, &[]) })?; f.write_str(&s) }) @@ -38,8 +37,8 @@ impl<'tcx> fmt::Debug for ty::AdtDef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { with_no_trimmed_paths!({ - let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |cx| { - cx.print_def_path(self.did(), &[]) + let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |p| { + p.print_def_path(self.did(), &[]) })?; f.write_str(&s) }) @@ -168,15 +167,11 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // If this is a value, we spend some effort to make it look nice. if let ConstKind::Value(cv) = self.kind() { - return ty::tls::with(move |tcx| { - let cv = tcx.lift(cv).unwrap(); - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?; - f.write_str(&cx.into_buffer()) - }); + write!(f, "{}", cv) + } else { + // Fall back to something verbose. + write!(f, "{:?}", self.kind()) } - // Fall back to something verbose. - write!(f, "{:?}", self.kind()) } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 80607978861..755fc68d86f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -821,10 +821,38 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_coroutine_witness( tcx: TyCtxt<'tcx>, - id: DefId, + def_id: DefId, args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - Ty::new(tcx, CoroutineWitness(id, args)) + if cfg!(debug_assertions) { + tcx.debug_assert_args_compatible(tcx.typeck_root_def_id(def_id), args); + } + Ty::new(tcx, CoroutineWitness(def_id, args)) + } + + pub fn new_coroutine_witness_for_coroutine( + tcx: TyCtxt<'tcx>, + def_id: DefId, + coroutine_args: GenericArgsRef<'tcx>, + ) -> Ty<'tcx> { + tcx.debug_assert_args_compatible(def_id, coroutine_args); + // HACK: Coroutine witness types are lifetime erased, so they + // never reference any lifetime args from the coroutine. We erase + // the regions here since we may get into situations where a + // coroutine is recursively contained within itself, leading to + // witness types that differ by region args. This means that + // cycle detection in fulfillment will not kick in, which leads + // to unnecessary overflows in async code. See the issue: + // <https://github.com/rust-lang/rust/issues/145151>. + let args = + ty::GenericArgs::for_item(tcx, tcx.typeck_root_def_id(def_id), |def, _| { + match def.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => coroutine_args[def.index as usize], + } + }); + Ty::new_coroutine_witness(tcx, def_id, args) } // misc @@ -985,6 +1013,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { Ty::new_coroutine_witness(interner, def_id, args) } + fn new_coroutine_witness_for_coroutine( + interner: TyCtxt<'tcx>, + def_id: DefId, + coroutine_args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_witness_for_coroutine(interner, def_id, coroutine_args) + } + fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self { Ty::new_ptr(interner, ty, mutbl) } @@ -1456,7 +1492,7 @@ impl<'tcx> Ty<'tcx> { } } - /// Returns the type and mutability of `*ty`. + /// Returns the type of `*ty`. /// /// The parameter `explicit` indicates if this is an *explicit* dereference. /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 6b187c5325a..8dd80aab946 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -206,10 +206,25 @@ pub struct TypeckResults<'tcx> { /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, + /// Goals proven during HIR typeck which may be potentially region dependent. + /// + /// Borrowck *uniquifies* regions which may cause these goal to be ambiguous in MIR + /// type check. We ICE if goals fail in borrowck to detect bugs during MIR building or + /// missed checks in HIR typeck. To avoid ICE due to region dependence we store all + /// goals which may be region dependent and reprove them in case borrowck encounters + /// an error. + pub potentially_region_dependent_goals: + FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, + /// Contains the data for evaluating the effect of feature `capture_disjoint_fields` /// on closure size. pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>, + /// Stores the types involved in calls to `transmute` intrinsic. These are meant to be checked + /// outside of typeck and borrowck to avoid cycles with opaque types and coroutine layout + /// computation. + pub transmutes_to_check: Vec<(Ty<'tcx>, Ty<'tcx>, HirId)>, + /// Container types and field indices of `offset_of!` expressions offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>, } @@ -240,7 +255,9 @@ impl<'tcx> TypeckResults<'tcx> { closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), coroutine_stalled_predicates: Default::default(), + potentially_region_dependent_goals: Default::default(), closure_size_eval: Default::default(), + transmutes_to_check: Default::default(), offset_of_data: Default::default(), } } |
