From 537f531b4ea648d4be0e53db00a5a6c15745bdac Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Fri, 10 May 2024 12:41:52 -0700 Subject: [PATCH 01/17] Promote `arm64ec-pc-windows-msvc` to tier 2 --- library/std/src/os/linux/raw.rs | 6 +++++- src/ci/github-actions/jobs.yml | 1 + src/doc/rustc/src/platform-support.md | 2 +- .../src/platform-support/arm64ec-pc-windows-msvc.md | 11 ++++++----- src/tools/build-manifest/src/main.rs | 1 + 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index c29dd62bc06f0..d53674d3c5f2c 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -244,7 +244,11 @@ mod arch { pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; } -#[cfg(target_arch = "aarch64")] +#[cfg(any( + target_arch = "aarch64", + // Arm64EC is Windows-only, but docs are always build as Linux, so re-use AArch64 for Arm64EC. + all(doc, target_arch = "arm64ec") +))] mod arch { use crate::os::raw::{c_int, c_long}; diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index ca86f3f0110ec..44ac555831a44 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -421,6 +421,7 @@ auto: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc --host=aarch64-pc-windows-msvc + --target=aarch64-pc-windows-msvc,arm64ec-pc-windows-msvc --enable-full-tools --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 77859956c95fa..53a8ba6d3d53b 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -146,6 +146,7 @@ target | std | notes [`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android `arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 `arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat +[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC [`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian [`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat `armv5te-unknown-linux-gnueabi` | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23) @@ -240,7 +241,6 @@ target | std | host | notes -------|:---:|:----:|------- [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin -[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ? | | Arm64EC Windows MSVC [`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | | Apple Catalyst on ARM64 [`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS [`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS Simulator diff --git a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md index 2e82753586261..dcabd21a83ebd 100644 --- a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md @@ -1,6 +1,6 @@ # `arm64ec-pc-windows-msvc` -**Tier: 3** +**Tier: 2** Arm64EC ("Emulation Compatible") for mixed architecture (AArch64 and x86_64) applications on AArch64 Windows 11. See . @@ -21,6 +21,9 @@ Only supported backend is LLVM 18 or above: * 18.1.4 fixed linking issue for some intrinsics implemented in `compiler_builtins`. +Visual Studio 2022 (or above) with the "ARM64/ARM64EC built tools" component and +the Windows 11 SDK are required. + ### Reusing code from other architectures - x86_64 or AArch64? Arm64EC uses `arm64ec` as its `target_arch`, but it is possible to reuse @@ -62,10 +65,8 @@ target = [ "arm64ec-pc-windows-msvc" ] ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy using `build-std` or -similar. +These targets are distributed through `rustup`, and otherwise require no +special configuration. ## Testing diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index bed76263b4516..9f33e4312740f 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -71,6 +71,7 @@ static TARGETS: &[&str] = &[ "arm-unknown-linux-gnueabihf", "arm-unknown-linux-musleabi", "arm-unknown-linux-musleabihf", + "arm64ec-pc-windows-msvc", "armv5te-unknown-linux-gnueabi", "armv5te-unknown-linux-musleabi", "armv7-linux-androideabi", From bfb7757c3c059d3bbcfbc604ab326fa777be13aa Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 10 Jun 2024 14:32:50 +0100 Subject: [PATCH 02/17] Correct parent for nested anon consts --- .../src/collect/generics_of.rs | 26 +++++++++++++++---- .../repeat_expr_hack_gives_right_generics.rs | 20 ++++++++++++++ ...peat_expr_hack_gives_right_generics.stderr | 11 ++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs create mode 100644 tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index abdf85ad707bc..303fa23dbc1e8 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -11,6 +11,7 @@ use rustc_session::lint; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; +#[instrument(level = "debug", skip(tcx))] pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { use rustc_hir::*; @@ -66,7 +67,22 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // FIXME(#43408) always enable this once `lazy_normalization` is // stable enough and does not need a feature gate anymore. Node::AnonConst(_) => { - let parent_def_id = tcx.hir().get_parent_item(hir_id); + let parent_did = tcx.parent(def_id.to_def_id()); + + // We don't do this unconditionally because the `DefId` parent of an anon const + // might be an implicitly created closure during `async fn` desugaring. This would + // have the wrong generics. + // + // i.e. `async fn foo<'a>() { let a = [(); { 1 + 2 }]; bar().await() }` + // would implicitly have a closure in its body that would be the parent of + // the `{ 1 + 2 }` anon const. This closure's generics is simply a witness + // instead of `['a]`. + let parent_did = if let DefKind::AnonConst = tcx.def_kind(parent_did) { + parent_did + } else { + tcx.hir().get_parent_item(hir_id).to_def_id() + }; + debug!(?parent_did); let mut in_param_ty = false; for (_parent, node) in tcx.hir().parent_iter(hir_id) { @@ -121,7 +137,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // // This has some implications for how we get the predicates available to the anon const // see `explicit_predicates_of` for more information on this - let generics = tcx.generics_of(parent_def_id.to_def_id()); + let generics = tcx.generics_of(parent_did); let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()]; // In the above example this would be .params[..N#0] let own_params = generics.params_to(param_def_idx as usize, tcx).to_owned(); @@ -147,7 +163,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // // Note that we do not supply the parent generics when using // `min_const_generics`. - Some(parent_def_id.to_def_id()) + Some(parent_did) } } else { let parent_node = tcx.parent_hir_node(hir_id); @@ -159,7 +175,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. }) if constant.hir_id() == hir_id => { - Some(parent_def_id.to_def_id()) + Some(parent_did) } // Exclude `GlobalAsm` here which cannot have generics. Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) @@ -171,7 +187,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { _ => false, }) => { - Some(parent_def_id.to_def_id()) + Some(parent_did) } _ => None, } diff --git a/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs new file mode 100644 index 0000000000000..899db191ae7d7 --- /dev/null +++ b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs @@ -0,0 +1,20 @@ +// Given an anon const `a`: `{ N }` and some anon const `b` which references the +// first anon const: `{ [1; a] }`. `b` should not have any generics as it is not +// a simple `N` argument nor is it a repeat expr count. +// +// On the other hand `b` *is* a repeat expr count and so it should inherit its +// parents generics as part of the `const_evaluatable_unchecked` fcw (#76200). +// +// In this specific case however `b`'s parent should be `a` and so it should wind +// up not having any generics after all. If `a` were to inherit its generics from +// the enclosing item then the reference to `a` from `b` would contain generic +// parameters not usable by `b` which would cause us to ICE. + +fn bar() {} + +fn foo() { + bar::<{ [1; N] }>(); + //~^ ERROR: generic parameters may not be used in const operations +} + +fn main() {} diff --git a/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr new file mode 100644 index 0000000000000..64548cc5a301e --- /dev/null +++ b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr @@ -0,0 +1,11 @@ +error: generic parameters may not be used in const operations + --> $DIR/repeat_expr_hack_gives_right_generics.rs:16:17 + | +LL | bar::<{ [1; N] }>(); + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: aborting due to 1 previous error + From af4d6c74efb31e34f75dfe5faf6064a0ff9cbd7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jun 2024 17:24:36 +0200 Subject: [PATCH 03/17] interpret: refactor dyn trait handling We can check that the vtable is for the right trait very early, and then just pass the type around. --- .../rustc_const_eval/src/interpret/cast.rs | 9 +-- .../rustc_const_eval/src/interpret/memory.rs | 19 ++++-- .../rustc_const_eval/src/interpret/place.rs | 49 -------------- .../src/interpret/terminator.rs | 37 +++++------ .../rustc_const_eval/src/interpret/traits.rs | 66 ++++++++++++++----- .../src/interpret/validity.rs | 18 ++--- .../rustc_const_eval/src/interpret/visitor.rs | 4 +- 7 files changed, 90 insertions(+), 112 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 19414c72c6a42..1ac899f9aa549 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -383,7 +383,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { let ptr = self.read_pointer(src)?; - // u64 cast is from usize to u64, which is always good let val = Immediate::new_slice( ptr, length.eval_target_usize(*self.tcx, self.param_env), @@ -401,13 +400,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (old_data, old_vptr) = val.to_scalar_pair(); let old_data = old_data.to_pointer(self)?; let old_vptr = old_vptr.to_pointer(self)?; - let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; - if old_trait != data_a.principal() { - throw_ub!(InvalidVTableTrait { - expected_trait: data_a, - vtable_trait: old_trait, - }); - } + let ty = self.get_ptr_vtable_ty(old_vptr, Some(data_a))?; let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 521f28b7123ea..84c6dad1cd3f3 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -867,19 +867,28 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) } - pub fn get_ptr_vtable( + /// Get the dynamic type of the given vtable pointer. + /// If `expected_trait` is `Some`, it must be a vtable for the given trait. + pub fn get_ptr_vtable_ty( &self, ptr: Pointer>, - ) -> InterpResult<'tcx, (Ty<'tcx>, Option>)> { + expected_trait: Option<&'tcx ty::List>>, + ) -> InterpResult<'tcx, Ty<'tcx>> { trace!("get_ptr_vtable({:?})", ptr); let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; if offset.bytes() != 0 { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) } - match self.tcx.try_get_global_alloc(alloc_id) { - Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)), - _ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))), + let Some(GlobalAlloc::VTable(ty, vtable_trait)) = self.tcx.try_get_global_alloc(alloc_id) + else { + throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) + }; + if let Some(expected_trait) = expected_trait { + if vtable_trait != expected_trait.principal() { + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } } + Ok(ty) } pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 4a86ec3f57a4a..a0630d7f3b5d2 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -9,7 +9,6 @@ use tracing::{instrument, trace}; use rustc_ast::Mutability; use rustc_middle::mir; -use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; use rustc_middle::{bug, span_bug}; @@ -1017,54 +1016,6 @@ where let layout = self.layout_of(raw.ty)?; Ok(self.ptr_to_mplace(ptr.into(), layout)) } - - /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. - /// Aso returns the vtable. - pub(super) fn unpack_dyn_trait( - &self, - mplace: &MPlaceTy<'tcx, M::Provenance>, - expected_trait: &'tcx ty::List>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer>)> { - assert!( - matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), - "`unpack_dyn_trait` only makes sense on `dyn*` types" - ); - let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; - let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; - if expected_trait.principal() != vtable_trait { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); - } - // This is a kind of transmute, from a place with unsized type and metadata to - // a place with sized type and no metadata. - let layout = self.layout_of(ty)?; - let mplace = - MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout }; - Ok((mplace, vtable)) - } - - /// Turn a `dyn* Trait` type into an value with the actual dynamic type. - /// Also returns the vtable. - pub(super) fn unpack_dyn_star>( - &self, - val: &P, - expected_trait: &'tcx ty::List>, - ) -> InterpResult<'tcx, (P, Pointer>)> { - assert!( - matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), - "`unpack_dyn_star` only makes sense on `dyn*` types" - ); - let data = self.project_field(val, 0)?; - let vtable = self.project_field(val, 1)?; - let vtable = self.read_pointer(&vtable.to_op(self)?)?; - let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; - if expected_trait.principal() != vtable_trait { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); - } - // `data` is already the right thing but has the wrong type. So we transmute it. - let layout = self.layout_of(ty)?; - let data = data.transmute(layout, self)?; - Ok((data, vtable)) - } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index cbfe25ca8df51..8f76a1486795e 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use either::Either; +use rustc_middle::ty::TyCtxt; use tracing::trace; use rustc_middle::span_bug; @@ -827,20 +828,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Obtain the underlying trait we are working on, and the adjusted receiver argument. - let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = + let (dyn_trait, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let (recv, vptr) = self.unpack_dyn_star(&receiver_place, data)?; - let (dyn_ty, _dyn_trait) = self.get_ptr_vtable(vptr)?; + let recv = self.unpack_dyn_star(&receiver_place, data)?; - (vptr, dyn_ty, recv.ptr()) + (data.principal(), recv.layout.ty, recv.ptr()) } else { // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. // (For that reason we also cannot use `unpack_dyn_trait`.) let receiver_tail = self .tcx .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); - let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else { + let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { span_bug!( self.cur_span(), "dynamic call on non-`dyn` type {}", @@ -851,25 +851,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Get the required information from the vtable. let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?; - let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; - if dyn_trait != data.principal() { - throw_ub!(InvalidVTableTrait { - expected_trait: data, - vtable_trait: dyn_trait, - }); - } + let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?; // It might be surprising that we use a pointer as the receiver even if this // is a by-val case; this works because by-val passing of an unsized `dyn // Trait` to a function is actually desugared to a pointer. - (vptr, dyn_ty, receiver_place.ptr()) + (receiver_trait.principal(), dyn_ty, receiver_place.ptr()) }; // Now determine the actual method to call. We can do that in two different ways and // compare them to ensure everything fits. - let Some(ty::VtblEntry::Method(fn_inst)) = - self.get_vtable_entries(vptr)?.get(idx).copied() - else { + let vtable_entries = if let Some(dyn_trait) = dyn_trait { + let trait_ref = dyn_trait.with_self_ty(*self.tcx, dyn_ty); + let trait_ref = self.tcx.erase_regions(trait_ref); + self.tcx.vtable_entries(trait_ref) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + }; + let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else { // FIXME(fee1-dead) these could be variants of the UB info enum instead of this throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method); }; @@ -898,7 +897,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty); args[0] = FnArg::Copy( ImmTy::from_immediate( - Scalar::from_maybe_pointer(adjusted_receiver, self).into(), + Scalar::from_maybe_pointer(adjusted_recv, self).into(), self.layout_of(receiver_ty)?, ) .into(), @@ -974,11 +973,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let place = match place.layout.ty.kind() { ty::Dynamic(data, _, ty::Dyn) => { // Dropping a trait object. Need to find actual drop fn. - self.unpack_dyn_trait(&place, data)?.0 + self.unpack_dyn_trait(&place, data)? } ty::Dynamic(data, _, ty::DynStar) => { // Dropping a `dyn*`. Need to find actual drop fn. - self.unpack_dyn_star(&place, data)?.0 + self.unpack_dyn_star(&place, data)? } _ => { debug_assert_eq!( diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 244a6ba48a4d3..1df9f5f7b2a25 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,11 +1,11 @@ use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, Size}; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, Machine}; +use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -33,28 +33,58 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(vtable_ptr.into()) } - /// Returns a high-level representation of the entries of the given vtable. - pub fn get_vtable_entries( - &self, - vtable: Pointer>, - ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { - let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; - Ok(if let Some(poly_trait_ref) = poly_trait_ref { - let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); - let trait_ref = self.tcx.erase_regions(trait_ref); - self.tcx.vtable_entries(trait_ref) - } else { - TyCtxt::COMMON_VTABLE_ENTRIES - }) - } - pub fn get_vtable_size_and_align( &self, vtable: Pointer>, ) -> InterpResult<'tcx, (Size, Align)> { - let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; + let ty = self.get_ptr_vtable_ty(vtable, None)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) } + + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. + pub(super) fn unpack_dyn_trait( + &self, + mplace: &MPlaceTy<'tcx, M::Provenance>, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + assert!( + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + "`unpack_dyn_trait` only makes sense on `dyn*` types" + ); + let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // This is a kind of transmute, from a place with unsized type and metadata to + // a place with sized type and no metadata. + let layout = self.layout_of(ty)?; + let mplace = mplace.offset_with_meta( + Size::ZERO, + OffsetMode::Wrapping, + MemPlaceMeta::None, + layout, + self, + )?; + Ok(mplace) + } + + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. + pub(super) fn unpack_dyn_star>( + &self, + val: &P, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx, P> { + assert!( + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + "`unpack_dyn_star` only makes sense on `dyn*` types" + ); + let data = self.project_field(val, 0)?; + let vtable = self.project_field(val, 1)?; + let vtable = self.read_pointer(&vtable.to_op(self)?)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // `data` is already the right thing but has the wrong type. So we transmute it. + let layout = self.layout_of(ty)?; + let data = data.transmute(layout, self)?; + Ok(data) + } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 3407c7b8c7925..92462e17da8a3 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -343,20 +343,16 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { match tail.kind() { ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; - // Make sure it is a genuine vtable pointer. - let (_dyn_ty, dyn_trait) = try_validation!( - self.ecx.get_ptr_vtable(vtable), + // Make sure it is a genuine vtable pointer for the right trait. + try_validation!( + self.ecx.get_ptr_vtable_ty(vtable, Some(data)), self.path, Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) => - InvalidVTablePtr { value: format!("{vtable}") } + InvalidVTablePtr { value: format!("{vtable}") }, + Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { + InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + }, ); - // Make sure it is for the right trait. - if dyn_trait != data.principal() { - throw_validation_failure!( - self.path, - InvalidMetaWrongTrait { expected_trait: data, vtable_trait: dyn_trait } - ); - } } ty::Slice(..) | ty::Str => { let _len = meta.unwrap_meta().to_target_usize(self.ecx)?; diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index b812e89854b20..71c057e549b41 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -95,7 +95,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // unsized values are never immediate, so we can assert_mem_place let op = v.to_op(self.ecx())?; let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?.0; + let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?; trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type return self.visit_field(v, 0, &inner_mplace.into()); @@ -104,7 +104,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // DynStar types. Very different from a dyn type (but strangely part of the // same variant in `TyKind`): These are pairs where the 2nd component is the // vtable, and the first component is the data (which must be ptr-sized). - let data = self.ecx().unpack_dyn_star(v, data)?.0; + let data = self.ecx().unpack_dyn_star(v, data)?; return self.visit_field(v, 0, &data); } // Slices do not need special handling here: they have `Array` field From 6e3134972ed18e095c27378287b85c0ad455e35a Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Mon, 10 Jun 2024 12:37:32 -0700 Subject: [PATCH 04/17] Simplify provider api to improve llvm ir --- library/core/src/error.rs | 49 ++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index da18fdc6e1d1f..042a8c9925f38 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -404,9 +404,9 @@ fn request_by_type_tag<'a, I>(err: &'a (impl Error + ?Sized)) -> Option, { - let mut tagged = TaggedOption::<'a, I>(None); + let mut tagged = Tagged { tag_id: TypeId::of::(), value: TaggedOption::<'a, I>(None) }; err.provide(tagged.as_request()); - tagged.0 + tagged.value.0 } /////////////////////////////////////////////////////////////////////////////// @@ -507,16 +507,9 @@ where /// #[unstable(feature = "error_generic_member_access", issue = "99301")] #[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -pub struct Request<'a>(dyn Erased<'a> + 'a); +pub struct Request<'a>(Tagged + 'a>); impl<'a> Request<'a> { - /// Create a new `&mut Request` from a `&mut dyn Erased` trait object. - fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Request<'a> { - // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Request<'a>` is safe since - // `Request` is repr(transparent). - unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Request<'a>) } - } - /// Provide a value or other type with only static lifetimes. /// /// # Examples @@ -940,27 +933,28 @@ pub(crate) mod tags { #[repr(transparent)] pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option); -impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { +impl<'a, I: tags::Type<'a>> Tagged> { pub(crate) fn as_request(&mut self) -> &mut Request<'a> { - Request::new(self as &mut (dyn Erased<'a> + 'a)) + let erased = self as &mut Tagged + 'a>; + // SAFETY: transmuting `&mut Tagged + 'a>` to `&mut Request<'a>` is safe since + // `Request` is repr(transparent). + unsafe { &mut *(erased as *mut Tagged> as *mut Request<'a>) } } } /// Represents a type-erased but identifiable object. /// /// This trait is exclusively implemented by the `TaggedOption` type. -unsafe trait Erased<'a>: 'a { - /// The `TypeId` of the erased type. - fn tag_id(&self) -> TypeId; -} +unsafe trait Erased<'a>: 'a {} -unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { - fn tag_id(&self) -> TypeId { - TypeId::of::() - } +unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {} + +struct Tagged { + tag_id: TypeId, + value: E, } -impl<'a> dyn Erased<'a> + 'a { +impl<'a> Tagged + 'a> { /// Returns some reference to the dynamic value if it is tagged with `I`, /// or `None` otherwise. #[inline] @@ -968,9 +962,9 @@ impl<'a> dyn Erased<'a> + 'a { where I: tags::Type<'a>, { - if self.tag_id() == TypeId::of::() { + if self.tag_id == TypeId::of::() { // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &*(self as *const Self).cast::>() }) + Some(&unsafe { &*(self as *const Self).cast::>>() }.value) } else { None } @@ -983,9 +977,12 @@ impl<'a> dyn Erased<'a> + 'a { where I: tags::Type<'a>, { - if self.tag_id() == TypeId::of::() { - // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &mut *(self as *mut Self).cast::>() }) + if self.tag_id == TypeId::of::() { + Some( + // SAFETY: Just checked whether we're pointing to an I. + &mut unsafe { &mut *(self as *mut Self).cast::>>() } + .value, + ) } else { None } From d041b7cf30bb5b5236cd9148cde7a3017ed28679 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 11 Jun 2024 07:47:58 +0200 Subject: [PATCH 05/17] check for correct trait in size_and_align_of --- compiler/rustc_const_eval/src/interpret/eval_context.rs | 4 ++-- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 6 ++++-- compiler/rustc_const_eval/src/interpret/traits.rs | 3 ++- src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7c2100fcbe38a..e28cc05cc2a80 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -765,10 +765,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Ok(Some((full_size, full_align))) } - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(expected_trait, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.get_vtable_size_and_align(vtable)?)) + Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) } ty::Slice(_) | ty::Str => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 18b76443cd954..497ae50014470 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -432,12 +432,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::vtable_size => { let ptr = self.read_pointer(&args[0])?; - let (size, _align) = self.get_vtable_size_and_align(ptr)?; + // `None` because we don't know which trait to expect here; any vtable is okay. + let (size, _align) = self.get_vtable_size_and_align(ptr, None)?; self.write_scalar(Scalar::from_target_usize(size.bytes(), self), dest)?; } sym::vtable_align => { let ptr = self.read_pointer(&args[0])?; - let (_size, align) = self.get_vtable_size_and_align(ptr)?; + // `None` because we don't know which trait to expect here; any vtable is okay. + let (_size, align) = self.get_vtable_size_and_align(ptr, None)?; self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 1df9f5f7b2a25..44e7244a513e3 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -36,8 +36,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn get_vtable_size_and_align( &self, vtable: Pointer>, + expected_trait: Option<&'tcx ty::List>>, ) -> InterpResult<'tcx, (Size, Align)> { - let ty = self.get_ptr_vtable_ty(vtable, None)?; + let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs index f71df9a1c909d..982d57b737202 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs @@ -1,5 +1,5 @@ -// Validation stops this too early. -//@compile-flags: -Zmiri-disable-validation +// Validation and SB stop this too early. +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows trait T1 { #[allow(dead_code)] From 3757136d8e0d8ddca294453e5a5ce70cfa3417e9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jun 2024 17:26:36 +0200 Subject: [PATCH 06/17] interpret: dyn trait metadata check: equate traits in a proper way --- .../rustc_const_eval/src/interpret/memory.rs | 4 +- .../rustc_const_eval/src/interpret/traits.rs | 36 ++++++++++++++++- ...iri-3541-dyn-vtable-trait-normalization.rs | 40 +++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 84c6dad1cd3f3..e2e39399f3a9f 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -884,9 +884,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) }; if let Some(expected_trait) = expected_trait { - if vtable_trait != expected_trait.principal() { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); - } + self.check_vtable_for_type(vtable_trait, expected_trait)?; } Ok(ty) } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 44e7244a513e3..bd2c651942185 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,11 +1,14 @@ +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, Size}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; +use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -44,6 +47,37 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok((layout.size, layout.align.abi)) } + /// Check that the given vtable trait is valid for a pointer/reference/place with the given + /// expected trait type. + pub(super) fn check_vtable_for_type( + &self, + vtable_trait: Option>, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx> { + // Fast path: if they are equal, it's all fine. + if expected_trait.principal() == vtable_trait { + return Ok(()); + } + if let (Some(expected_trait), Some(vtable_trait)) = + (expected_trait.principal(), vtable_trait) + { + // Slow path: spin up an inference context to check if these traits are sufficiently equal. + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let cause = ObligationCause::dummy_with_span(self.cur_span()); + // equate the two trait refs after normalization + let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait); + let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait); + if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() { + if ocx.select_all_or_error().is_empty() { + // All good. + return Ok(()); + } + } + } + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. pub(super) fn unpack_dyn_trait( &self, diff --git a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs new file mode 100644 index 0000000000000..c46031de2d84f --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs @@ -0,0 +1,40 @@ +#![feature(ptr_metadata)] +// This test is the result of minimizing the `emplacable` crate to reproduce +// . + +use std::{ops::FnMut, ptr::Pointee}; + +pub type EmplacerFn<'a, T> = dyn for<'b> FnMut(::Metadata) + 'a; + +#[repr(transparent)] +pub struct Emplacer<'a, T>(EmplacerFn<'a, T>) +where + T: ?Sized; + +impl<'a, T> Emplacer<'a, T> +where + T: ?Sized, +{ + pub unsafe fn from_fn<'b>(emplacer_fn: &'b mut EmplacerFn<'a, T>) -> &'b mut Self { + // This used to trigger: + // constructing invalid value: wrong trait in wide pointer vtable: expected + // `std::ops::FnMut(<[std::boxed::Box] as std::ptr::Pointee>::Metadata)`, but encountered + // `std::ops::FnMut<(usize,)>`. + unsafe { &mut *((emplacer_fn as *mut EmplacerFn<'a, T>) as *mut Self) } + } +} + +pub fn box_new_with() +where + T: ?Sized, +{ + let emplacer_closure = &mut |_meta| { + unreachable!(); + }; + + unsafe { Emplacer::::from_fn(emplacer_closure) }; +} + +fn main() { + box_new_with::<[Box]>(); +} From 0fc18e3a17a4dd061189d83c4805e3aaf7c83cf0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 6 Jun 2024 10:08:55 -0400 Subject: [PATCH 07/17] Remove DebugWithInfcx --- compiler/rustc_middle/src/ty/list.rs | 10 +- compiler/rustc_middle/src/ty/mod.rs | 1 - .../rustc_middle/src/ty/structural_impls.rs | 103 ++---------- compiler/rustc_type_ir/src/binder.rs | 15 +- compiler/rustc_type_ir/src/const_kind.rs | 50 +----- compiler/rustc_type_ir/src/debug.rs | 146 ------------------ compiler/rustc_type_ir/src/inherent.rs | 10 +- compiler/rustc_type_ir/src/interner.rs | 10 +- compiler/rustc_type_ir/src/lib.rs | 2 - compiler/rustc_type_ir/src/predicate.rs | 32 +--- compiler/rustc_type_ir/src/region_kind.rs | 30 +--- compiler/rustc_type_ir/src/ty_kind.rs | 107 ++++--------- ...await.b-{closure#0}.coroutine_resume.0.mir | 30 ++-- .../occurs-check/associated-type.next.stderr | 8 +- .../occurs-check/associated-type.old.stderr | 16 +- .../structually-relate-aliases.stderr | 4 +- .../issue-118950-root-region.stderr | 8 +- 17 files changed, 105 insertions(+), 477 deletions(-) delete mode 100644 compiler/rustc_type_ir/src/debug.rs diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 0db541899d20e..71a93cc520d5b 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -1,5 +1,5 @@ use super::flags::FlagComputation; -use super::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, TyCtxt, TypeFlags, WithInfcx}; +use super::{DebruijnIndex, TypeFlags}; use crate::arena::Arena; use rustc_data_structures::aligned::{align_of, Aligned}; use rustc_serialize::{Encodable, Encoder}; @@ -162,14 +162,6 @@ impl fmt::Debug for RawList { (**self).fmt(f) } } -impl<'tcx, H, T: DebugWithInfcx>> DebugWithInfcx> for RawList { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - fmt::Debug::fmt(&this.map(|this| this.as_slice()), f) - } -} impl> Encodable for RawList { #[inline] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7ff1b79982274..83f8de6b6f93f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -61,7 +61,6 @@ use rustc_span::{ExpnId, ExpnKind, Span}; use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx}; pub use rustc_target::abi::{ReprFlags, ReprOptions}; pub use rustc_type_ir::relate::VarianceDiagInfo; -pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx}; use tracing::{debug, instrument}; pub use vtable::*; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index cc6b1d57f8705..71e2e3e9f9942 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -13,7 +13,7 @@ use rustc_ast_ir::visit::VisitorResult; use rustc_hir::def::Namespace; use rustc_span::source_map::Spanned; use rustc_target::abi::TyAndLayout; -use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, WithInfcx}; +use rustc_type_ir::ConstKind; use std::fmt::{self, Debug}; @@ -83,14 +83,6 @@ impl fmt::Debug for ty::LateParamRegion { } } -impl<'tcx> ty::DebugWithInfcx> for Ty<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - this.data.fmt(f) - } -} impl<'tcx> fmt::Debug for Ty<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { with_no_trimmed_paths!(fmt::Debug::fmt(self.kind(), f)) @@ -121,70 +113,33 @@ impl<'tcx> fmt::Debug for ty::Clause<'tcx> { } } -impl<'tcx> DebugWithInfcx> for Pattern<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match &**this.data { - ty::PatternKind::Range { start, end, include_end } => f - .debug_struct("Pattern::Range") - .field("start", start) - .field("end", end) - .field("include_end", include_end) - .finish(), - } - } -} - impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl<'tcx> DebugWithInfcx> for ty::consts::Expr<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.data.kind { + match self.kind { ty::ExprKind::Binop(op) => { - let (lhs_ty, rhs_ty, lhs, rhs) = this.data.binop_args(); - write!( - f, - "({op:?}: ({:?}: {:?}), ({:?}: {:?}))", - &this.wrap(lhs), - &this.wrap(lhs_ty), - &this.wrap(rhs), - &this.wrap(rhs_ty), - ) + let (lhs_ty, rhs_ty, lhs, rhs) = self.binop_args(); + write!(f, "({op:?}: ({:?}: {:?}), ({:?}: {:?}))", lhs, lhs_ty, rhs, rhs_ty,) } ty::ExprKind::UnOp(op) => { - let (rhs_ty, rhs) = this.data.unop_args(); - write!(f, "({op:?}: ({:?}: {:?}))", &this.wrap(rhs), &this.wrap(rhs_ty)) + let (rhs_ty, rhs) = self.unop_args(); + write!(f, "({op:?}: ({:?}: {:?}))", rhs, rhs_ty) } ty::ExprKind::FunctionCall => { - let (func_ty, func, args) = this.data.call_args(); + let (func_ty, func, args) = self.call_args(); let args = args.collect::>(); - write!(f, "({:?}: {:?})(", &this.wrap(func), &this.wrap(func_ty))?; + write!(f, "({:?}: {:?})(", func, func_ty)?; for arg in args.iter().rev().skip(1).rev() { - write!(f, "{:?}, ", &this.wrap(arg))?; + write!(f, "{:?}, ", arg)?; } if let Some(arg) = args.last() { - write!(f, "{:?}", &this.wrap(arg))?; + write!(f, "{:?}", arg)?; } write!(f, ")") } ty::ExprKind::Cast(kind) => { - let (value_ty, value, to_ty) = this.data.cast_args(); - write!( - f, - "({kind:?}: ({:?}: {:?}), {:?})", - &this.wrap(value), - &this.wrap(value_ty), - &this.wrap(to_ty) - ) + let (value_ty, value, to_ty) = self.cast_args(); + write!(f, "({kind:?}: ({:?}: {:?}), {:?})", value, value_ty, to_ty) } } } @@ -192,20 +147,12 @@ impl<'tcx> DebugWithInfcx> for ty::consts::Expr<'tcx> { impl<'tcx> fmt::Debug for ty::Const<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl<'tcx> DebugWithInfcx> for ty::Const<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { // If this is a value, we spend some effort to make it look nice. - if let ConstKind::Value(_, _) = this.data.kind() { + if let ConstKind::Value(_, _) = self.kind() { return ty::tls::with(move |tcx| { // Somehow trying to lift the valtree results in lifetime errors, so we lift the // entire constant. - let lifted = tcx.lift(*this.data).unwrap(); + let lifted = tcx.lift(*self).unwrap(); let ConstKind::Value(ty, valtree) = lifted.kind() else { bug!("we checked that this is a valtree") }; @@ -215,7 +162,7 @@ impl<'tcx> DebugWithInfcx> for ty::Const<'tcx> { }); } // Fall back to something verbose. - write!(f, "{kind:?}", kind = &this.map(|data| data.kind())) + write!(f, "{:?}", self.kind()) } } @@ -247,32 +194,12 @@ impl<'tcx> fmt::Debug for GenericArg<'tcx> { } } } -impl<'tcx> DebugWithInfcx> for GenericArg<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.data.unpack() { - GenericArgKind::Lifetime(lt) => write!(f, "{:?}", &this.wrap(lt)), - GenericArgKind::Const(ct) => write!(f, "{:?}", &this.wrap(ct)), - GenericArgKind::Type(ty) => write!(f, "{:?}", &this.wrap(ty)), - } - } -} impl<'tcx> fmt::Debug for Region<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.kind()) } } -impl<'tcx> DebugWithInfcx> for Region<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - write!(f, "{:?}", &this.map(|data| data.kind())) - } -} /////////////////////////////////////////////////////////////////////////// // Atomic structs diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 9a2c9059967b1..e50d59ba5f0e6 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -8,12 +8,11 @@ use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_serialize::Decodable; use tracing::debug; -use crate::debug::{DebugWithInfcx, WithInfcx}; use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; use crate::inherent::*; use crate::lift::Lift; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use crate::{self as ty, InferCtxtLike, Interner, SsoHashSet}; +use crate::{self as ty, Interner, SsoHashSet}; /// Binder is a binder for higher-ranked lifetimes or types. It is part of the /// compiler's representation for things like `for<'a> Fn(&'a isize)` @@ -56,18 +55,6 @@ where } } -impl> DebugWithInfcx for ty::Binder { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - f.debug_tuple("Binder") - .field(&this.map(|data| data.as_ref().skip_binder())) - .field(&this.data.bound_vars()) - .finish() - } -} - macro_rules! impl_binder_encode_decode { ($($t:ty),+ $(,)?) => { $( diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 84d48e14c2414..f1683f5449f5c 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -5,7 +5,7 @@ use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt; -use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{self as ty, DebruijnIndex, Interner}; use self::ConstKind::*; @@ -61,28 +61,19 @@ impl PartialEq for ConstKind { impl fmt::Debug for ConstKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} - -impl DebugWithInfcx for ConstKind { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { use ConstKind::*; - match this.data { + match self { Param(param) => write!(f, "{param:?}"), - Infer(var) => write!(f, "{:?}", &this.wrap(var)), + Infer(var) => write!(f, "{:?}", &var), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), Unevaluated(uv) => { - write!(f, "{:?}", &this.wrap(uv)) + write!(f, "{:?}", &uv) } - Value(ty, valtree) => write!(f, "({valtree:?}: {:?})", &this.wrap(ty)), + Value(ty, valtree) => write!(f, "({valtree:?}: {:?})", &ty), Error(_) => write!(f, "{{const error}}"), - Expr(expr) => write!(f, "{:?}", &this.wrap(expr)), + Expr(expr) => write!(f, "{:?}", &expr), } } } @@ -112,17 +103,9 @@ impl UnevaluatedConst { impl fmt::Debug for UnevaluatedConst { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for UnevaluatedConst { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { f.debug_struct("UnevaluatedConst") - .field("def", &this.data.def) - .field("args", &this.wrap(this.data.args)) + .field("def", &self.def) + .field("args", &self.args) .finish() } } @@ -175,23 +158,6 @@ impl fmt::Debug for InferConst { } } } -impl DebugWithInfcx for InferConst { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match *this.data { - InferConst::Var(vid) => match this.infcx.universe_of_ct(vid) { - None => write!(f, "{:?}", this.data), - Some(universe) => write!(f, "?{}_{}c", vid.index(), universe.index()), - }, - InferConst::EffectVar(vid) => write!(f, "?{}e", vid.index()), - InferConst::Fresh(_) => { - unreachable!() - } - } - } -} #[cfg(feature = "nightly")] impl HashStable for InferConst { diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs deleted file mode 100644 index c206f3ccdb5b2..0000000000000 --- a/compiler/rustc_type_ir/src/debug.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::{ - ConstVid, EffectVid, FloatVid, InferCtxtLike, IntVid, Interner, RegionVid, TyVid, UniverseIndex, -}; - -use core::fmt; -use std::marker::PhantomData; - -pub struct NoInfcx(PhantomData); - -impl InferCtxtLike for NoInfcx { - type Interner = I; - - fn interner(&self) -> Self::Interner { - unreachable!() - } - - fn universe_of_ty(&self, _ty: TyVid) -> Option { - None - } - - fn universe_of_lt(&self, _lt: RegionVid) -> Option { - None - } - - fn universe_of_ct(&self, _ct: ConstVid) -> Option { - None - } - - fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> I::Ty { - panic!("cannot resolve {vid:?}") - } - - fn opportunistic_resolve_int_var(&self, vid: IntVid) -> I::Ty { - panic!("cannot resolve {vid:?}") - } - - fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> I::Ty { - panic!("cannot resolve {vid:?}") - } - - fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> I::Const { - panic!("cannot resolve {vid:?}") - } - - fn opportunistic_resolve_effect_var(&self, vid: EffectVid) -> I::Const { - panic!("cannot resolve {vid:?}") - } - - fn opportunistic_resolve_lt_var(&self, vid: crate::RegionVid) -> I::Region { - panic!("cannot resolve {vid:?}") - } - - fn defining_opaque_types(&self) -> I::DefiningOpaqueTypes { - Default::default() - } -} - -pub trait DebugWithInfcx: fmt::Debug { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result; -} - -impl + ?Sized> DebugWithInfcx for &'_ T { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - >::fmt(this.map(|&data| data), f) - } -} - -impl> DebugWithInfcx for [T] { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match f.alternate() { - true => { - write!(f, "[\n")?; - for element in this.data.iter() { - write!(f, "{:?},\n", &this.wrap(element))?; - } - write!(f, "]") - } - false => { - write!(f, "[")?; - if this.data.len() > 0 { - for element in &this.data[..(this.data.len() - 1)] { - write!(f, "{:?}, ", &this.wrap(element))?; - } - if let Some(element) = this.data.last() { - write!(f, "{:?}", &this.wrap(element))?; - } - } - write!(f, "]") - } - } - } -} - -pub struct WithInfcx<'a, Infcx: InferCtxtLike, T> { - pub data: T, - pub infcx: &'a Infcx, -} - -impl Copy for WithInfcx<'_, Infcx, T> {} - -impl Clone for WithInfcx<'_, Infcx, T> { - fn clone(&self) -> Self { - Self { data: self.data.clone(), infcx: self.infcx } - } -} - -impl<'a, I: Interner, T> WithInfcx<'a, NoInfcx, T> { - pub fn with_no_infcx(data: T) -> Self { - Self { data, infcx: &NoInfcx(PhantomData) } - } -} - -impl<'a, Infcx: InferCtxtLike, T> WithInfcx<'a, Infcx, T> { - pub fn new(data: T, infcx: &'a Infcx) -> Self { - Self { data, infcx } - } - - pub fn wrap(self, u: U) -> WithInfcx<'a, Infcx, U> { - WithInfcx { data: u, infcx: self.infcx } - } - - pub fn map(self, f: impl FnOnce(T) -> U) -> WithInfcx<'a, Infcx, U> { - WithInfcx { data: f(self.data), infcx: self.infcx } - } - - pub fn as_ref(&self) -> WithInfcx<'a, Infcx, &T> { - WithInfcx { data: &self.data, infcx: self.infcx } - } -} - -impl> fmt::Debug - for WithInfcx<'_, Infcx, T> -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - DebugWithInfcx::fmt(self.as_ref(), f) - } -} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 205a1e5f100e7..2fc765f1c8fae 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -12,11 +12,11 @@ use rustc_ast_ir::Mutability; use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{self as ty, CollectAndApply, DebugWithInfcx, Interner, UpcastFrom}; +use crate::{self as ty, CollectAndApply, Interner, UpcastFrom}; pub trait Ty>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + Into @@ -116,7 +116,7 @@ pub trait Safety>: Copy + Debug + Hash + Eq + TypeVis pub trait Region>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + Into @@ -134,7 +134,7 @@ pub trait Region>: pub trait Const>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + Into @@ -166,7 +166,7 @@ pub trait GenericsOf> { pub trait GenericArgs>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + IntoIterator diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index ad1d2753b2822..b7f412ecb8ea9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -9,7 +9,7 @@ use crate::ir_print::IrPrint; use crate::relate::Relate; use crate::solve::inspect::CanonicalGoalEvaluationStep; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{self as ty, DebugWithInfcx}; +use crate::{self as ty}; pub trait Interner: Sized @@ -32,7 +32,7 @@ pub trait Interner: type GenericArgs: GenericArgs; type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref; type GenericArg: Copy - + DebugWithInfcx + + Debug + Hash + Eq + IntoKind> @@ -74,9 +74,9 @@ pub trait Interner: // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; - type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Eq + Relate; + type BoundExistentialPredicates: Copy + Debug + Hash + Eq + Relate; type AllocId: Copy + Debug + Hash + Eq; - type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx + Relate; + type Pat: Copy + Debug + Hash + Eq + Debug + Relate; type Safety: Safety + TypeFoldable + Relate; type Abi: Abi + TypeFoldable + Relate; @@ -86,7 +86,7 @@ pub trait Interner: type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike; type ValueConst: Copy + Debug + Hash + Eq; - type ExprConst: Copy + DebugWithInfcx + Hash + Eq + Relate; + type ExprConst: Copy + Debug + Hash + Eq + Relate; // Kinds of regions type Region: Region; diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 7371646893035..caf1afb5246e3 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -40,7 +40,6 @@ mod macros; mod binder; mod canonical; mod const_kind; -mod debug; mod flags; mod generic_arg; mod infcx; @@ -57,7 +56,6 @@ pub use canonical::*; #[cfg(feature = "nightly")] pub use codec::*; pub use const_kind::*; -pub use debug::{DebugWithInfcx, WithInfcx}; pub use flags::*; pub use generic_arg::*; pub use infcx::InferCtxtLike; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 63a4c2e9d1f76..c0713dc50d269 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -9,7 +9,7 @@ use crate::inherent::*; use crate::lift::Lift; use crate::upcast::Upcast; use crate::visit::TypeVisitableExt as _; -use crate::{self as ty, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{self as ty, Interner}; /// `A: 'region` #[derive(derivative::Derivative)] @@ -248,16 +248,6 @@ pub enum ExistentialPredicate { AutoTrait(I::DefId), } -// FIXME: Implement this the right way after -impl DebugWithInfcx for ExistentialPredicate { - fn fmt>( - this: rustc_type_ir::WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - fmt::Debug::fmt(&this.data, f) - } -} - impl ty::Binder> { /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` @@ -459,7 +449,8 @@ impl AliasTermKind { Copy(bound = ""), Hash(bound = ""), PartialEq(bound = ""), - Eq(bound = "") + Eq(bound = ""), + Debug(bound = "") )] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] @@ -493,23 +484,6 @@ pub struct AliasTerm { _use_alias_term_new_instead: (), } -impl std::fmt::Debug for AliasTerm { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for AliasTerm { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - f.debug_struct("AliasTerm") - .field("args", &this.map(|data| data.args)) - .field("def_id", &this.data.def_id) - .finish() - } -} - impl AliasTerm { pub fn new( interner: I, diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 48ade27328943..37c9532ad8940 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -4,7 +4,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use std::fmt; -use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{DebruijnIndex, Interner}; use self::RegionKind::*; @@ -18,18 +18,6 @@ rustc_index::newtype_index! { pub struct RegionVid {} } -impl DebugWithInfcx for RegionVid { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.infcx.universe_of_lt(*this.data) { - Some(universe) => write!(f, "'?{}_{}", this.data.index(), universe.index()), - None => write!(f, "{:?}", this.data), - } - } -} - /// Representation of regions. Note that the NLL checker uses a distinct /// representation of regions. For this reason, it internally replaces all the /// regions with inference variables -- the index of the variable is then used @@ -230,12 +218,9 @@ impl PartialEq for RegionKind { } } -impl DebugWithInfcx for RegionKind { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.data { +impl fmt::Debug for RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { ReEarlyParam(data) => write!(f, "{data:?}"), ReBound(binder_id, bound_region) => { @@ -247,7 +232,7 @@ impl DebugWithInfcx for RegionKind { ReStatic => f.write_str("'static"), - ReVar(vid) => write!(f, "{:?}", &this.wrap(vid)), + ReVar(vid) => write!(f, "{:?}", &vid), RePlaceholder(placeholder) => write!(f, "{placeholder:?}"), @@ -260,11 +245,6 @@ impl DebugWithInfcx for RegionKind { } } } -impl fmt::Debug for RegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} #[cfg(feature = "nightly")] // This is not a derived impl because a derive would require `I: HashStable` diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 71f3862226d82..8b4ad2f5ed0ff 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -10,7 +10,7 @@ use std::fmt; pub use self::closure::*; use self::TyKind::*; use crate::inherent::*; -use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{self as ty, DebruijnIndex, Interner}; use rustc_ast_ir::Mutability; @@ -341,12 +341,10 @@ impl PartialEq for TyKind { } } -impl DebugWithInfcx for TyKind { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match this.data { +// This is manually implemented because a derive would require `I: Debug` +impl fmt::Debug for TyKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { Bool => write!(f, "bool"), Char => write!(f, "char"), Int(i) => write!(f, "{i:?}"), @@ -369,27 +367,23 @@ impl DebugWithInfcx for TyKind { } Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), - Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)), - Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &this.wrap(t), &this.wrap(p)), - Slice(t) => write!(f, "[{:?}]", &this.wrap(t)), - RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), this.wrap(ty)), - Ref(r, t, m) => write!(f, "&{:?} {}{:?}", this.wrap(r), m.prefix_str(), this.wrap(t)), - FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&this.wrap(s)).finish(), - FnPtr(s) => write!(f, "{:?}", &this.wrap(s)), + Array(t, c) => write!(f, "[{:?}; {:?}]", &t, &c), + Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &t, &p), + Slice(t) => write!(f, "[{:?}]", &t), + RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), + Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), + FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&s).finish(), + FnPtr(s) => write!(f, "{:?}", &s), Dynamic(p, r, repr) => match repr { - DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &this.wrap(p), &this.wrap(r)), + DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &p, &r), DynKind::DynStar => { - write!(f, "dyn* {:?} + {:?}", &this.wrap(p), &this.wrap(r)) + write!(f, "dyn* {:?} + {:?}", &p, &r) } }, - Closure(d, s) => f.debug_tuple("Closure").field(d).field(&this.wrap(s)).finish(), - CoroutineClosure(d, s) => { - f.debug_tuple("CoroutineClosure").field(d).field(&this.wrap(s)).finish() - } - Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&this.wrap(s)).finish(), - CoroutineWitness(d, s) => { - f.debug_tuple("CoroutineWitness").field(d).field(&this.wrap(s)).finish() - } + Closure(d, s) => f.debug_tuple("Closure").field(d).field(&s).finish(), + CoroutineClosure(d, s) => f.debug_tuple("CoroutineClosure").field(d).field(&s).finish(), + Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&s).finish(), + CoroutineWitness(d, s) => f.debug_tuple("CoroutineWitness").field(d).field(&s).finish(), Never => write!(f, "!"), Tuple(t) => { write!(f, "(")?; @@ -398,7 +392,7 @@ impl DebugWithInfcx for TyKind { if count > 0 { write!(f, ", ")?; } - write!(f, "{:?}", &this.wrap(ty))?; + write!(f, "{:?}", &ty)?; count += 1; } // unary tuples need a trailing comma @@ -407,23 +401,16 @@ impl DebugWithInfcx for TyKind { } write!(f, ")") } - Alias(i, a) => f.debug_tuple("Alias").field(i).field(&this.wrap(a)).finish(), + Alias(i, a) => f.debug_tuple("Alias").field(i).field(&a).finish(), Param(p) => write!(f, "{p:?}"), Bound(d, b) => crate::debug_bound_var(f, *d, b), Placeholder(p) => write!(f, "{p:?}"), - Infer(t) => write!(f, "{:?}", this.wrap(t)), + Infer(t) => write!(f, "{:?}", t), TyKind::Error(_) => write!(f, "{{type error}}"), } } } -// This is manually implemented because a derive would require `I: Debug` -impl fmt::Debug for TyKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} - /// Represents the projection of an associated, opaque, or lazy-type-alias type. /// /// * For a projection, this would be `>::N<...>`. @@ -435,7 +422,8 @@ impl fmt::Debug for TyKind { Copy(bound = ""), Hash(bound = ""), PartialEq(bound = ""), - Eq(bound = "") + Eq(bound = ""), + Debug(bound = "") )] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] @@ -555,23 +543,6 @@ impl AliasTy { } } -impl fmt::Debug for AliasTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for AliasTy { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - f.debug_struct("AliasTy") - .field("args", &this.map(|data| data.args)) - .field("def_id", &this.data.def_id) - .finish() - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum IntTy { @@ -968,24 +939,6 @@ impl fmt::Debug for InferTy { } } -impl DebugWithInfcx for InferTy { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match this.data { - InferTy::TyVar(vid) => { - if let Some(universe) = this.infcx.universe_of_ty(*vid) { - write!(f, "?{}_{}t", vid.index(), universe.index()) - } else { - write!(f, "{:?}", this.data) - } - } - _ => write!(f, "{:?}", this.data), - } - } -} - #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), @@ -1078,15 +1031,7 @@ impl ty::Binder> { impl fmt::Debug for FnSig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for FnSig { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - let sig = this.data; + let sig = self; let FnSig { inputs_and_output: _, c_variadic, safety, abi } = sig; write!(f, "{}", safety.prefix_str())?; @@ -1100,7 +1045,7 @@ impl DebugWithInfcx for FnSig { if i > 0 { write!(f, ", ")?; } - write!(f, "{:?}", &this.wrap(ty))?; + write!(f, "{:?}", &ty)?; } if *c_variadic { if inputs.is_empty() { @@ -1113,7 +1058,7 @@ impl DebugWithInfcx for FnSig { match output.kind() { Tuple(list) if list.is_empty() => Ok(()), - _ => write!(f, " -> {:?}", &this.wrap(sig.output())), + _ => write!(f, " -> {:?}", sig.output()), } } } diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index d2a0fb0cb3c3d..c0f16ee7ec0b4 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -5,12 +5,15 @@ ty: Coroutine( DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), [ - (), - std::future::ResumeTy, - (), - (), - CoroutineWitness(DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), []), - (), + (), + std::future::ResumeTy, + (), + (), + CoroutineWitness( + DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + [], + ), + (), ], ), source_info: SourceInfo { @@ -23,12 +26,15 @@ ty: Coroutine( DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), [ - (), - std::future::ResumeTy, - (), - (), - CoroutineWitness(DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), []), - (), + (), + std::future::ResumeTy, + (), + (), + CoroutineWitness( + DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + [], + ), + (), ], ), source_info: SourceInfo { diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index d809a6948f3ea..f32bb1301da59 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,7 +1,7 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index 329086ab7dfdf..d26f7665ee733 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,11 +1,11 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr index 2f1dfd19c483b..e1cffa0fc376c 100644 --- a/tests/ui/higher-ranked/structually-relate-aliases.stderr +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -1,5 +1,5 @@ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), _use_alias_ty_new_instead: () } error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied --> $DIR/structually-relate-aliases.rs:13:36 | diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index 17da1f5247963..04c44276195d8 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -25,10 +25,10 @@ help: this trait has no implementations, consider adding one LL | trait ToUnit<'a> { | ^^^^^^^^^^^^^^^^ - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () } error[E0119]: conflicting implementations of trait `Overlap` for type `fn(_)` --> $DIR/issue-118950-root-region.rs:19:1 | From 5d8f40a63afe3999df61c2487555a540b6ff30ea Mon Sep 17 00:00:00 2001 From: Lin Yihai Date: Tue, 11 Jun 2024 17:31:06 +0800 Subject: [PATCH 08/17] No uninitalized report in a pre-returned match arm --- .../src/diagnostics/conflict_errors.rs | 21 ++++++++++++++---- .../uninitalized-in-match-arm-issue-126133.rs | 22 +++++++++++++++++++ ...nitalized-in-match-arm-issue-126133.stderr | 15 +++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs create mode 100644 tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 821a903665479..f32ff57fe88b9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -557,8 +557,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // for the branching codepaths that aren't covered, to point at them. let map = self.infcx.tcx.hir(); let body = map.body_owned_by(self.mir_def_id()); - - let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; + let mut visitor = + ConditionVisitor { tcx: self.infcx.tcx, spans: &spans, name: &name, errors: vec![] }; visitor.visit_body(&body); let mut show_assign_sugg = false; @@ -4372,13 +4372,14 @@ impl<'hir> Visitor<'hir> for BreakFinder { /// Given a set of spans representing statements initializing the relevant binding, visit all the /// function expressions looking for branching code paths that *do not* initialize the binding. -struct ConditionVisitor<'b> { +struct ConditionVisitor<'b, 'tcx> { + tcx: TyCtxt<'tcx>, spans: &'b [Span], name: &'b str, errors: Vec<(Span, String)>, } -impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { +impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> { fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { match ex.kind { hir::ExprKind::If(cond, body, None) => { @@ -4464,6 +4465,12 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { ), )); } else if let Some(guard) = &arm.guard { + if matches!( + self.tcx.hir_node(arm.body.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) + ) { + continue; + } self.errors.push(( arm.pat.span.to(guard.span), format!( @@ -4473,6 +4480,12 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { ), )); } else { + if matches!( + self.tcx.hir_node(arm.body.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) + ) { + continue; + } self.errors.push(( arm.pat.span, format!( diff --git a/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs new file mode 100644 index 0000000000000..1974bbf9fe7b6 --- /dev/null +++ b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs @@ -0,0 +1,22 @@ +enum E { + A, + B, + C, +} + +fn foo(e: E) { + let bar; + + match e { + E::A if true => return, + E::A => return, + E::B => {} + E::C => { + bar = 5; + } + } + + let _baz = bar; //~ ERROR E0381 +} + +fn main() {} diff --git a/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr new file mode 100644 index 0000000000000..eb7d0f8b2047f --- /dev/null +++ b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr @@ -0,0 +1,15 @@ +error[E0381]: used binding `bar` is possibly-uninitialized + --> $DIR/uninitalized-in-match-arm-issue-126133.rs:19:16 + | +LL | let bar; + | --- binding declared here but left uninitialized +... +LL | E::B => {} + | ---- if this pattern is matched, `bar` is not initialized +... +LL | let _baz = bar; + | ^^^ `bar` used here but it is possibly-uninitialized + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0381`. From 3bbd480f07dcc3a8b9516232d041eada63b10a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 12 Jun 2024 11:45:52 +0200 Subject: [PATCH 09/17] Update `rustc-perf` submodule To integrate https://github.com/rust-lang/rustc-perf/pull/1926. --- src/tools/rustc-perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf index 72daa50ce2350..c64bb60dd1636 160000 --- a/src/tools/rustc-perf +++ b/src/tools/rustc-perf @@ -1 +1 @@ -Subproject commit 72daa50ce2350f5a9b5ae6dc3ad6babccd14ec0a +Subproject commit c64bb60dd1636922b1ccbb82867bed934a99dbcb From cae18fa7b0a773e967e5b8ef6fbccdaa7e0b6eed Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 12 Jun 2024 13:15:02 +0200 Subject: [PATCH 10/17] Clarify doc comment. --- library/core/src/panic/panic_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index df8f441bf3533..a593abc86058a 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -72,7 +72,7 @@ impl<'a> PanicInfo<'a> { /// Returns the payload associated with the panic. /// - /// On `core::panic::PanicInfo`, this method never returns anything useful. + /// On this type, `core::panic::PanicInfo`, this method never returns anything useful. /// It only exists because of compatibility with [`std::panic::PanicHookInfo`], /// which used to be the same type. /// From 1dc795230320d231c8c870ffd25ae17c2f7ea8ed Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 12 Jun 2024 13:15:17 +0200 Subject: [PATCH 11/17] Fix deprecation version. --- library/core/src/panic/panic_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index a593abc86058a..1925f8e8eac96 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -80,7 +80,7 @@ impl<'a> PanicInfo<'a> { /// /// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html /// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload - #[deprecated(since = "1.77.0", note = "this never returns anything useful")] + #[deprecated(since = "CURRENT_RUSTC_VERSION", note = "this never returns anything useful")] #[stable(feature = "panic_hooks", since = "1.10.0")] #[allow(deprecated, deprecated_in_future)] pub fn payload(&self) -> &(dyn crate::any::Any + Send) { From 2944eab5e67b732f994df4fdefaa43e9ef225682 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 12 Jun 2024 13:15:29 +0200 Subject: [PATCH 12/17] Use payload_as_str instead of two downcasts. --- library/std/src/panic.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 5282c00fccaf0..c5d1a893ee809 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -202,10 +202,7 @@ impl fmt::Display for PanicHookInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; - if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - formatter.write_str(":\n")?; - formatter.write_str(payload)?; - } else if let Some(payload) = self.payload.downcast_ref::() { + if let Some(payload) = self.payload_as_str() { formatter.write_str(":\n")?; formatter.write_str(payload)?; } From 9afc91349e508121a2cc1d15402736b09a2527b6 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 12 Jun 2024 13:17:42 +0200 Subject: [PATCH 13/17] Update doc comment on PanicInfo::message(). --- library/core/src/panic/panic_info.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 1925f8e8eac96..c8e507c690e29 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -29,9 +29,8 @@ impl<'a> PanicInfo<'a> { PanicInfo { location, message, can_unwind, force_no_backtrace } } - /// If the `panic!` macro from the `core` crate (not from `std`) - /// was used with a formatting string and some additional arguments, - /// returns that message ready to be used for example with [`fmt::write`] + /// The message that was given to the `panic!` macro, + /// ready to be formatted with e.g. [`fmt::write`]. #[must_use] #[unstable(feature = "panic_info_message", issue = "66745")] pub fn message(&self) -> fmt::Arguments<'_> { From 749a685626be4bd42eb1772eb497b6006f48cb14 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 12 Jun 2024 13:23:21 +0200 Subject: [PATCH 14/17] Fix deprecated version. --- library/core/src/panic/panic_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index c8e507c690e29..78cf1d2e98eca 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -79,7 +79,7 @@ impl<'a> PanicInfo<'a> { /// /// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html /// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload - #[deprecated(since = "CURRENT_RUSTC_VERSION", note = "this never returns anything useful")] + #[deprecated(since = "1.81.0", note = "this never returns anything useful")] #[stable(feature = "panic_hooks", since = "1.10.0")] #[allow(deprecated, deprecated_in_future)] pub fn payload(&self) -> &(dyn crate::any::Any + Send) { From dc6def3042074f38e7c6bdaba3038fb2b1f0e2aa Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 11 Jun 2024 22:13:40 +1000 Subject: [PATCH 15/17] coverage: Add `tests/coverage/assert-ne.rs` This test extracts a fragment of `issue-84561.rs` that has historically proven troublesome when trying to modify how spans are extracted from MIR. --- tests/coverage/assert-ne.cov-map | 13 +++++++++++++ tests/coverage/assert-ne.coverage | 23 +++++++++++++++++++++++ tests/coverage/assert-ne.rs | 22 ++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tests/coverage/assert-ne.cov-map create mode 100644 tests/coverage/assert-ne.coverage create mode 100644 tests/coverage/assert-ne.rs diff --git a/tests/coverage/assert-ne.cov-map b/tests/coverage/assert-ne.cov-map new file mode 100644 index 0000000000000..6d9906fd7f573 --- /dev/null +++ b/tests/coverage/assert-ne.cov-map @@ -0,0 +1,13 @@ +Function name: assert_ne::main +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 08, 01, 03, 1c, 05, 04, 0d, 00, 13, 02, 02, 0d, 00, 13, 09, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 8, 1) to (start + 3, 28) +- Code(Counter(1)) at (prev + 4, 13) to (start + 0, 19) +- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 19) + = (c0 - c1) +- Code(Counter(2)) at (prev + 3, 5) to (start + 1, 2) + diff --git a/tests/coverage/assert-ne.coverage b/tests/coverage/assert-ne.coverage new file mode 100644 index 0000000000000..236a8fd138588 --- /dev/null +++ b/tests/coverage/assert-ne.coverage @@ -0,0 +1,23 @@ + LL| |//@ edition: 2021 + LL| | + LL| |use core::hint::black_box; + LL| | + LL| |#[derive(Debug, PartialEq)] + LL| |struct Foo(u32); + LL| | + LL| 1|fn main() { + LL| 1| assert_ne!( + LL| 1| Foo(5), // Make sure this expression's span isn't lost. + LL| 1| if black_box(false) { + LL| 0| Foo(0) // + LL| | } else { + LL| 1| Foo(1) // + LL| | } + LL| | ); + LL| 1| () + LL| 1|} + LL| | + LL| |// This test is a short fragment extracted from `issue-84561.rs`, highlighting + LL| |// a particular span of code that can easily be lost if overlapping spans are + LL| |// processed incorrectly. + diff --git a/tests/coverage/assert-ne.rs b/tests/coverage/assert-ne.rs new file mode 100644 index 0000000000000..8a8fe0898048d --- /dev/null +++ b/tests/coverage/assert-ne.rs @@ -0,0 +1,22 @@ +//@ edition: 2021 + +use core::hint::black_box; + +#[derive(Debug, PartialEq)] +struct Foo(u32); + +fn main() { + assert_ne!( + Foo(5), // Make sure this expression's span isn't lost. + if black_box(false) { + Foo(0) // + } else { + Foo(1) // + } + ); + () +} + +// This test is a short fragment extracted from `issue-84561.rs`, highlighting +// a particular span of code that can easily be lost if overlapping spans are +// processed incorrectly. From 0bfdb8d33dd89099fc2b573d3df4c2347ffa2dbb Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 12 Jun 2024 22:48:11 +1000 Subject: [PATCH 16/17] coverage: Add `tests/coverage/loop-break.rs` This is a modified copy of `tests/mir-opt/coverage/instrument_coverage.rs`. --- tests/coverage/loop-break.cov-map | 14 ++++++++++++++ tests/coverage/loop-break.coverage | 14 ++++++++++++++ tests/coverage/loop-break.rs | 13 +++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 tests/coverage/loop-break.cov-map create mode 100644 tests/coverage/loop-break.coverage create mode 100644 tests/coverage/loop-break.rs diff --git a/tests/coverage/loop-break.cov-map b/tests/coverage/loop-break.cov-map new file mode 100644 index 0000000000000..98df0fdab6b97 --- /dev/null +++ b/tests/coverage/loop-break.cov-map @@ -0,0 +1,14 @@ +Function name: loop_break::main +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 03, 01, 00, 0b, 03, 01, 05, 01, 27, 01, 02, 0d, 00, 12, 05, 01, 0a, 00, 0b, 01, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 11) +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 39) + = (c0 + c1) +- Code(Counter(0)) at (prev + 2, 13) to (start + 0, 18) +- Code(Counter(1)) at (prev + 1, 10) to (start + 0, 11) +- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) + diff --git a/tests/coverage/loop-break.coverage b/tests/coverage/loop-break.coverage new file mode 100644 index 0000000000000..cea7dee83f10b --- /dev/null +++ b/tests/coverage/loop-break.coverage @@ -0,0 +1,14 @@ + LL| |//@ edition: 2021 + LL| | + LL| 1|fn main() { + LL| 1| loop { + LL| 1| if core::hint::black_box(true) { + LL| 1| break; + LL| 0| } + LL| | } + LL| 1|} + LL| | + LL| |// This test is a lightly-modified version of `tests/mir-opt/coverage/instrument_coverage.rs`. + LL| |// If this test needs to be blessed, then the mir-opt version probably needs to + LL| |// be blessed too! + diff --git a/tests/coverage/loop-break.rs b/tests/coverage/loop-break.rs new file mode 100644 index 0000000000000..9a842225e8331 --- /dev/null +++ b/tests/coverage/loop-break.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 + +fn main() { + loop { + if core::hint::black_box(true) { + break; + } + } +} + +// This test is a lightly-modified version of `tests/mir-opt/coverage/instrument_coverage.rs`. +// If this test needs to be blessed, then the mir-opt version probably needs to +// be blessed too! From 2fa78f3a2a87a7522d3c95bd9c53aaeede63996e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 10 Jun 2024 01:33:33 +1000 Subject: [PATCH 17/17] coverage: Replace the old span refiner with a single function As more and more of the span refiner's functionality has been pulled out into separate early passes, it has finally reached the point where we can remove the rest of the old `SpansRefiner` code, and replace it with a single modestly-sized function. --- .../rustc_mir_transform/src/coverage/spans.rs | 239 +++--------------- tests/coverage/loop-break.cov-map | 6 +- tests/coverage/loop-break.coverage | 2 +- ...ment_coverage.main.InstrumentCoverage.diff | 2 +- 4 files changed, 41 insertions(+), 208 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 743f1cc24beee..bb6a666ff73b1 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,6 +1,5 @@ -use rustc_middle::bug; use rustc_middle::mir; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::mappings; @@ -23,7 +22,7 @@ pub(super) fn extract_refined_covspans( let sorted_span_buckets = from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks); for bucket in sorted_span_buckets { - let refined_spans = SpansRefiner::refine_sorted_spans(bucket); + let refined_spans = refine_sorted_spans(bucket); code_mappings.extend(refined_spans.into_iter().map(|RefinedCovspan { span, bcb }| { // Each span produced by the refiner represents an ordinary code region. mappings::CodeMapping { span, bcb } @@ -31,58 +30,6 @@ pub(super) fn extract_refined_covspans( } } -#[derive(Debug)] -struct CurrCovspan { - span: Span, - bcb: BasicCoverageBlock, -} - -impl CurrCovspan { - fn new(span: Span, bcb: BasicCoverageBlock) -> Self { - Self { span, bcb } - } - - fn into_prev(self) -> PrevCovspan { - let Self { span, bcb } = self; - PrevCovspan { span, bcb, merged_spans: vec![span] } - } -} - -#[derive(Debug)] -struct PrevCovspan { - span: Span, - bcb: BasicCoverageBlock, - /// List of all the original spans from MIR that have been merged into this - /// span. Mainly used to precisely skip over gaps when truncating a span. - merged_spans: Vec, -} - -impl PrevCovspan { - fn is_mergeable(&self, other: &CurrCovspan) -> bool { - self.bcb == other.bcb - } - - fn merge_from(&mut self, other: &CurrCovspan) { - debug_assert!(self.is_mergeable(other)); - self.span = self.span.to(other.span); - self.merged_spans.push(other.span); - } - - fn cutoff_statements_at(mut self, cutoff_pos: BytePos) -> Option { - self.merged_spans.retain(|span| span.hi() <= cutoff_pos); - if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() { - self.span = self.span.with_hi(max_hi); - } - - if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) } - } - - fn into_refined(self) -> RefinedCovspan { - let Self { span, bcb, merged_spans: _ } = self; - RefinedCovspan { span, bcb } - } -} - #[derive(Debug)] struct RefinedCovspan { span: Span, @@ -100,164 +47,50 @@ impl RefinedCovspan { } } -/// Converts the initial set of coverage spans (one per MIR `Statement` or `Terminator`) into a -/// minimal set of coverage spans, using the BCB CFG to determine where it is safe and useful to: -/// -/// * Remove duplicate source code coverage regions -/// * Merge spans that represent continuous (both in source code and control flow), non-branching -/// execution -struct SpansRefiner { - /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative - /// dominance between the `BasicCoverageBlock`s of equal `Span`s. - sorted_spans_iter: std::vec::IntoIter, - - /// The current coverage span to compare to its `prev`, to possibly merge, discard, - /// or cause `prev` to be modified or discarded. - /// If `curr` is not discarded or merged, it becomes `prev` for the next iteration. - some_curr: Option, - - /// The coverage span from a prior iteration; typically assigned from that iteration's `curr`. - /// If that `curr` was discarded, `prev` retains its value from the previous iteration. - some_prev: Option, - - /// The final coverage spans to add to the coverage map. A `Counter` or `Expression` - /// will also be injected into the MIR for each BCB that has associated spans. - refined_spans: Vec, -} - -impl SpansRefiner { - /// Takes the initial list of (sorted) spans extracted from MIR, and "refines" - /// them by merging compatible adjacent spans, removing redundant spans, - /// and carving holes in spans when they overlap in unwanted ways. - fn refine_sorted_spans(sorted_spans: Vec) -> Vec { - let sorted_spans_len = sorted_spans.len(); - let this = Self { - sorted_spans_iter: sorted_spans.into_iter(), - some_curr: None, - some_prev: None, - refined_spans: Vec::with_capacity(sorted_spans_len), - }; - - this.to_refined_spans() - } - - /// Iterate through the sorted coverage spans, and return the refined list of merged and - /// de-duplicated spans. - fn to_refined_spans(mut self) -> Vec { - while self.next_coverage_span() { - // For the first span we don't have `prev` set, so most of the - // span-processing steps don't make sense yet. - if self.some_prev.is_none() { - debug!(" initial span"); - continue; - } - - // The remaining cases assume that `prev` and `curr` are set. - let prev = self.prev(); - let curr = self.curr(); - - if prev.is_mergeable(curr) { - debug!(?prev, "curr will be merged into prev"); - let curr = self.take_curr(); - self.prev_mut().merge_from(&curr); - } else if prev.span.hi() <= curr.span.lo() { - debug!( - " different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}", - ); - let prev = self.take_prev().into_refined(); - self.refined_spans.push(prev); - } else { - self.cutoff_prev_at_overlapping_curr(); - } - } - - // There is usually a final span remaining in `prev` after the loop ends, - // so add it to the output as well. - if let Some(prev) = self.some_prev.take() { - debug!(" AT END, adding last prev={prev:?}"); - self.refined_spans.push(prev.into_refined()); - } - - // Do one last merge pass, to simplify the output. - self.refined_spans.dedup_by(|b, a| { - if a.is_mergeable(b) { - debug!(?a, ?b, "merging list-adjacent refined spans"); - a.merge_from(b); - true - } else { +/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines" +/// those spans by removing spans that overlap in unwanted ways, and by merging +/// compatible adjacent spans. +#[instrument(level = "debug")] +fn refine_sorted_spans(sorted_spans: Vec) -> Vec { + // Holds spans that have been read from the input vector, but haven't yet + // been committed to the output vector. + let mut pending = vec![]; + let mut refined = vec![]; + + for curr in sorted_spans { + pending.retain(|prev: &SpanFromMir| { + if prev.span.hi() <= curr.span.lo() { + // There's no overlap between the previous/current covspans, + // so move the previous one into the refined list. + refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb }); false + } else { + // Otherwise, retain the previous covspan only if it has the + // same BCB. This tends to discard long outer spans that enclose + // smaller inner spans with different control flow. + prev.bcb == curr.bcb } }); - - self.refined_spans - } - - #[track_caller] - fn curr(&self) -> &CurrCovspan { - self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)")) - } - - /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the - /// `curr` coverage span. - #[track_caller] - fn take_curr(&mut self) -> CurrCovspan { - self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)")) - } - - #[track_caller] - fn prev(&self) -> &PrevCovspan { - self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)")) - } - - #[track_caller] - fn prev_mut(&mut self) -> &mut PrevCovspan { - self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)")) + pending.push(curr); } - #[track_caller] - fn take_prev(&mut self) -> PrevCovspan { - self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)")) + // Drain the rest of the pending list into the refined list. + for prev in pending { + refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb }); } - /// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order. - fn next_coverage_span(&mut self) -> bool { - if let Some(curr) = self.some_curr.take() { - self.some_prev = Some(curr.into_prev()); - } - if let Some(SpanFromMir { span, bcb, .. }) = self.sorted_spans_iter.next() { - // This code only sees sorted spans after hole-carving, so there should - // be no way for `curr` to start before `prev`. - if let Some(prev) = &self.some_prev { - debug_assert!(prev.span.lo() <= span.lo()); - } - self.some_curr = Some(CurrCovspan::new(span, bcb)); - debug!(?self.some_prev, ?self.some_curr, "next_coverage_span"); + // Do one last merge pass, to simplify the output. + debug!(?refined, "before merge"); + refined.dedup_by(|b, a| { + if a.is_mergeable(b) { + debug!(?a, ?b, "merging list-adjacent refined spans"); + a.merge_from(b); true } else { false } - } + }); + debug!(?refined, "after merge"); - /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ - /// statements that end before `curr.lo()` (if any), and add the portion of the - /// combined span for those statements. Any other statements have overlapping spans - /// that can be ignored because `curr` and/or other upcoming statements/spans inside - /// the overlap area will produce their own counters. This disambiguation process - /// avoids injecting multiple counters for overlapping spans, and the potential for - /// double-counting. - fn cutoff_prev_at_overlapping_curr(&mut self) { - debug!( - " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ - if it has statements that end before curr; prev={:?}", - self.prev() - ); - - let curr_span = self.curr().span; - if let Some(prev) = self.take_prev().cutoff_statements_at(curr_span.lo()) { - debug!("after cutoff, adding {prev:?}"); - self.refined_spans.push(prev); - } else { - debug!("prev was eliminated by cutoff"); - } - } + refined } diff --git a/tests/coverage/loop-break.cov-map b/tests/coverage/loop-break.cov-map index 98df0fdab6b97..890d5d84539e4 100644 --- a/tests/coverage/loop-break.cov-map +++ b/tests/coverage/loop-break.cov-map @@ -1,14 +1,14 @@ Function name: loop_break::main -Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 03, 01, 00, 0b, 03, 01, 05, 01, 27, 01, 02, 0d, 00, 12, 05, 01, 0a, 00, 0b, 01, 02, 01, 00, 02] +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 03, 01, 00, 0b, 03, 02, 0c, 00, 27, 01, 01, 0d, 00, 12, 05, 01, 0a, 00, 0b, 01, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 11) -- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 39) +- Code(Expression(0, Add)) at (prev + 2, 12) to (start + 0, 39) = (c0 + c1) -- Code(Counter(0)) at (prev + 2, 13) to (start + 0, 18) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 18) - Code(Counter(1)) at (prev + 1, 10) to (start + 0, 11) - Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) diff --git a/tests/coverage/loop-break.coverage b/tests/coverage/loop-break.coverage index cea7dee83f10b..1b7c64fb68d5b 100644 --- a/tests/coverage/loop-break.coverage +++ b/tests/coverage/loop-break.coverage @@ -1,7 +1,7 @@ LL| |//@ edition: 2021 LL| | LL| 1|fn main() { - LL| 1| loop { + LL| | loop { LL| 1| if core::hint::black_box(true) { LL| 1| break; LL| 0| } diff --git a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff index 01876b494c5d0..a594c44c316af 100644 --- a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff @@ -9,7 +9,7 @@ + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Add, rhs: Counter(1) }; + coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:10:1 - 10:11; -+ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:11:5 - 12:17; ++ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:12:12 - 12:17; + coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:13:13 - 13:18; + coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:14:10 - 14:11; + coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:16:1 - 16:2;