From 7be47b219df42cba96560a97de707967cca8c762 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 15 Mar 2024 11:59:38 +0100 Subject: interpret/allocation: fix aliasing issue in interpreter and refactor getters a bit - rename mutating functions to be more scary - add a new raw bytes getter --- .../rustc_middle/src/mir/interpret/allocation.rs | 42 ++++++++++++++++------ 1 file changed, 31 insertions(+), 11 deletions(-) (limited to 'compiler/rustc_middle/src') diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 4047891d769..00faa211853 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -37,9 +37,16 @@ pub trait AllocBytes: /// Create a zeroed `AllocBytes` of the specified size and alignment. /// Returns `None` if we ran out of memory on the host. fn zeroed(size: Size, _align: Align) -> Option; + + /// Gives direct access to the raw underlying storage. + /// + /// Crucially this pointer is compatible with: + /// - other pointers retunred by this method, and + /// - references returned from `deref()`, as long as there was no write. + fn as_mut_ptr(&mut self) -> *mut u8; } -// Default `bytes` for `Allocation` is a `Box<[u8]>`. +/// Default `bytes` for `Allocation` is a `Box`. impl AllocBytes for Box<[u8]> { fn from_bytes<'a>(slice: impl Into>, _align: Align) -> Self { Box::<[u8]>::from(slice.into()) @@ -51,6 +58,11 @@ impl AllocBytes for Box<[u8]> { let bytes = unsafe { bytes.assume_init() }; Some(bytes) } + + fn as_mut_ptr(&mut self) -> *mut u8 { + // Carefully avoiding any intermediate references. + ptr::addr_of_mut!(**self).cast() + } } /// This type represents an Allocation in the Miri/CTFE core engine. @@ -399,10 +411,6 @@ impl Allocation /// Byte accessors. impl Allocation { - pub fn base_addr(&self) -> *const u8 { - self.bytes.as_ptr() - } - /// This is the entirely abstraction-violating way to just grab the raw bytes without /// caring about provenance or initialization. /// @@ -452,13 +460,14 @@ impl Allocation Ok(self.get_bytes_unchecked(range)) } - /// Just calling this already marks everything as defined and removes provenance, - /// so be sure to actually put data there! + /// This is the entirely abstraction-violating way to just get mutable access to the raw bytes. + /// Just calling this already marks everything as defined and removes provenance, so be sure to + /// actually overwrite all the data there! /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods /// on `InterpCx` instead. - pub fn get_bytes_mut( + pub fn get_bytes_unchecked_for_overwrite( &mut self, cx: &impl HasDataLayout, range: AllocRange, @@ -469,8 +478,9 @@ impl Allocation Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) } - /// A raw pointer variant of `get_bytes_mut` that avoids invalidating existing aliases into this memory. - pub fn get_bytes_mut_ptr( + /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases + /// into this memory. + pub fn get_bytes_unchecked_for_overwrite_ptr( &mut self, cx: &impl HasDataLayout, range: AllocRange, @@ -479,10 +489,19 @@ impl Allocation self.provenance.clear(range, cx)?; assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check + // Cruciall, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) } + + /// This gives direct mutable access to the entire buffer, just exposing their internal state + /// without reseting anything. Directly exposes `AllocBytes::as_mut_ptr`. Only works if + /// `OFFSET_IS_ADDR` is true. + pub fn get_bytes_unchecked_raw_mut(&mut self) -> *mut u8 { + assert!(Prov::OFFSET_IS_ADDR); + self.bytes.as_mut_ptr() + } } /// Reading and writing. @@ -589,7 +608,8 @@ impl Allocation }; let endian = cx.data_layout().endian; - let dst = self.get_bytes_mut(cx, range)?; + // Yes we do overwrite all the bytes in `dst`. + let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. -- cgit 1.4.1-3-g733a5 From 1cf345e10abec509b708eaa4c0458577624013d9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 21 Mar 2024 10:04:20 +0000 Subject: Remove unnecessary Partial/Ord impl --- compiler/rustc_middle/src/mir/consts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'compiler/rustc_middle/src') diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 214297b9869..02185cbeacf 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -456,7 +456,7 @@ impl<'tcx> Const<'tcx> { } /// An unevaluated (potentially generic) constant used in MIR. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)] #[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct UnevaluatedConst<'tcx> { pub def: DefId, -- cgit 1.4.1-3-g733a5 From cda209bf433605c0dcd08f4bd04057e6b5623a79 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 21 Mar 2024 10:11:44 +0000 Subject: Stop `ConstraintCategory` `Ord` impl from relying on `Ty`'s `Ord` impl. --- Cargo.lock | 1 + compiler/rustc_middle/Cargo.toml | 1 + compiler/rustc_middle/src/mir/query.rs | 12 ++++++++++-- ...-shorter-to-static-comparing-against-free.stderr | 19 +++++++++---------- .../nll/user-annotations/adt-nullary-enums.stderr | 21 +++++++++++---------- 5 files changed, 32 insertions(+), 22 deletions(-) (limited to 'compiler/rustc_middle/src') diff --git a/Cargo.lock b/Cargo.lock index 3110f32ade9..680cb500421 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4236,6 +4236,7 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags 2.4.2", + "derivative", "either", "field-offset", "gsgdt", diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 96c58ef411b..9e8ee92b5d9 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +derivative = "2.2.0" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 8bd872c1b19..731e050ca9b 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -284,8 +284,15 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// order of the category, thereby influencing diagnostic output. /// /// See also `rustc_const_eval::borrow_check::constraints`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] +#[derive(derivative::Derivative)] +#[derivative( + PartialOrd, + Ord, + PartialOrd = "feature_allow_slow_enum", + Ord = "feature_allow_slow_enum" +)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, @@ -295,6 +302,7 @@ pub enum ConstraintCategory<'tcx> { Cast { /// Whether this is an unsizing cast and if yes, this contains the target type. /// Region variables are erased to ReErased. + #[derivative(PartialOrd = "ignore", Ord = "ignore")] unsize_to: Option>, }, @@ -304,7 +312,7 @@ pub enum ConstraintCategory<'tcx> { ClosureBounds, /// Contains the function type if available. - CallArgument(Option>), + CallArgument(#[derivative(PartialOrd = "ignore", Ord = "ignore")] Option>), CopyBound, SizedBound, Assignment, diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 9feb2a7320d..d7933a39eaa 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -53,17 +53,16 @@ LL | fn case2() { error[E0597]: `a` does not live long enough --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:30:26 | -LL | let a = 0; - | - binding `a` declared here -LL | let cell = Cell::new(&a); - | ^^ borrowed value does not live long enough +LL | let a = 0; + | - binding `a` declared here +LL | let cell = Cell::new(&a); + | ----------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'static` ... -LL | / foo(cell, |cell_a, cell_x| { -LL | | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error -LL | | }) - | |______- argument requires that `a` is borrowed for `'static` -LL | } - | - `a` dropped here while still borrowed +LL | } + | - `a` dropped here while still borrowed error: aborting due to 2 previous errors diff --git a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr index 5b385feeedc..644fc94f730 100644 --- a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr +++ b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr @@ -1,16 +1,17 @@ error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:33:41 | -LL | let c = 66; - | - binding `c` declared here -LL | / combine( -LL | | SomeEnum::SomeVariant(Cell::new(&c)), - | | ^^ borrowed value does not live long enough -LL | | SomeEnum::SomeOtherVariant::>, -LL | | ); - | |_____- argument requires that `c` is borrowed for `'static` -LL | } - | - `c` dropped here while still borrowed +LL | let c = 66; + | - binding `c` declared here +LL | combine( +LL | SomeEnum::SomeVariant(Cell::new(&c)), + | ----------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +... +LL | } + | - `c` dropped here while still borrowed error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:41:41 -- cgit 1.4.1-3-g733a5 From d8470bb00b5ed2dff50a9bff26854cb75478bf0e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 21 Mar 2024 10:26:45 +0000 Subject: Sorting arbitrary constants should not be done, as it relies on `DefId` ordering, which breaks incremental compilation. --- compiler/rustc_middle/src/ty/layout.rs | 67 +------------------- tests/ui/const-generics/transmute-fail.rs | 75 ++++++++++++++++++++++ tests/ui/const-generics/transmute-fail.stderr | 91 +++++++++++++++++++++++++-- tests/ui/const-generics/transmute.rs | 69 -------------------- 4 files changed, 164 insertions(+), 138 deletions(-) (limited to 'compiler/rustc_middle/src') diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 11fd73c9094..595ef71cc32 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2,7 +2,7 @@ use crate::error::UnsupportedFnAbi; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; use crate::ty::normalize_erasing_regions::NormalizationError; -use crate::ty::{self, ConstKind, Ty, TyCtxt, TypeVisitableExt}; +use crate::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_error_messages::DiagMessage; use rustc_errors::{ Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, @@ -356,21 +356,10 @@ impl<'tcx> SizeSkeleton<'tcx> { .ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?; return Ok(SizeSkeleton::Known(Size::from_bytes(size))); } - let len = tcx.expand_abstract_consts(len); - let prev = ty::Const::from_target_usize(tcx, s.bytes()); - let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else { - return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty))); - }; - Ok(SizeSkeleton::Generic(gen_size)) + Err(tcx.arena.alloc(LayoutError::Unknown(ty))) } SizeSkeleton::Pointer { .. } => Err(err), - SizeSkeleton::Generic(g) => { - let len = tcx.expand_abstract_consts(len); - let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else { - return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty))); - }; - Ok(SizeSkeleton::Generic(gen_size)) - } + SizeSkeleton::Generic(_) => Err(tcx.arena.alloc(LayoutError::Unknown(ty))), } } @@ -468,56 +457,6 @@ impl<'tcx> SizeSkeleton<'tcx> { } } -/// When creating the layout for types with abstract consts in their size (i.e. [usize; 4 * N]), -/// to ensure that they have a canonical order and can be compared directly we combine all -/// constants, and sort the other terms. This allows comparison of expressions of sizes, -/// allowing for things like transmuting between types that depend on generic consts. -/// This returns `None` if multiplication of constants overflows. -fn mul_sorted_consts<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, -) -> Option> { - use crate::mir::BinOp::Mul; - - let mut work = vec![a, b]; - let mut done = vec![]; - while let Some(n) = work.pop() { - if let ConstKind::Expr(ty::Expr::Binop(Mul, l, r)) = n.kind() { - work.push(l); - work.push(r) - } else { - done.push(n); - } - } - let mut k = 1; - let mut overflow = false; - done.retain(|c| { - let Some(c) = c.try_eval_target_usize(tcx, param_env) else { - return true; - }; - let Some(next) = c.checked_mul(k) else { - overflow = true; - return false; - }; - k = next; - false - }); - if overflow { - return None; - } - if k != 1 { - done.push(ty::Const::from_target_usize(tcx, k)); - } else if k == 0 { - return Some(ty::Const::from_target_usize(tcx, 0)); - } - done.sort_unstable(); - - // create a single tree from the buffer - done.into_iter().reduce(|acc, n| ty::Const::new_expr(tcx, ty::Expr::Binop(Mul, n, acc), n.ty())) -} - pub trait HasTyCtxt<'tcx>: HasDataLayout { fn tcx(&self) -> TyCtxt<'tcx>; } diff --git a/tests/ui/const-generics/transmute-fail.rs b/tests/ui/const-generics/transmute-fail.rs index d7bf1b47fb5..90afd232534 100644 --- a/tests/ui/const-generics/transmute-fail.rs +++ b/tests/ui/const-generics/transmute-fail.rs @@ -32,4 +32,79 @@ fn overflow(v: [[[u32; 8888888]; 9999999]; 777777777]) -> [[[u32; 9999999]; 7777 } } +fn transpose(v: [[u32;H]; W]) -> [[u32; W]; H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn ident(v: [[u32; H]; W]) -> [[u32; H]; W] { + unsafe { + std::mem::transmute(v) + } +} + +fn flatten(v: [[u32; H]; W]) -> [u32; W * H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn coagulate(v: [u32; H*W]) -> [[u32; W];H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn flatten_3d( + v: [[[u32; D]; H]; W] +) -> [u32; D * W * H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn flatten_somewhat( + v: [[[u32; D]; H]; W] +) -> [[u32; D * W]; H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn known_size(v: [u16; L]) -> [u8; L * 2] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn condense_bytes(v: [u8; L * 2]) -> [u16; L] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn singleton_each(v: [u8; L]) -> [[u8;1]; L] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn transpose_with_const( + v: [[u32; 2 * H]; W + W] +) -> [[u32; W + W]; 2 * H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + fn main() {} diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 397e3be768a..1b0d1ea50d0 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -4,8 +4,8 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `[[u32; H+1]; W]` (generic size (H + 1) * 4 * W) - = note: target type: `[[u32; W+1]; H]` (generic size (W + 1) * 4 * H) + = note: source type: `[[u32; H+1]; W]` (size can vary because of [u32; H+1]) + = note: target type: `[[u32; W+1]; H]` (size can vary because of [u32; W+1]) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:16:5 @@ -22,8 +22,8 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `[[u32; H]; W]` (generic size 4 * H * W) - = note: target type: `[u32; W * H * H]` (generic size 4 * H * H * W) + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) + = note: target type: `[u32; W * H * H]` (this type does not have a fixed size) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:30:5 @@ -34,6 +34,87 @@ LL | std::mem::transmute(v) = note: source type: `[[[u32; 8888888]; 9999999]; 777777777]` (values of the type `[[u32; 8888888]; 9999999]` are too big for the current architecture) = note: target type: `[[[u32; 9999999]; 777777777]; 8888888]` (values of the type `[[u32; 9999999]; 777777777]` are too big for the current architecture) +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:37:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) + = note: target type: `[[u32; W]; H]` (size can vary because of [u32; W]) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:50:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) + = note: target type: `[u32; W * H]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:57:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u32; H*W]` (this type does not have a fixed size) + = note: target type: `[[u32; W]; H]` (size can vary because of [u32; W]) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:66:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[[u32; D]; H]; W]` (size can vary because of [u32; D]) + = note: target type: `[u32; D * W * H]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:75:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[[u32; D]; H]; W]` (size can vary because of [u32; D]) + = note: target type: `[[u32; D * W]; H]` (size can vary because of [u32; D * W]) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:82:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u16; L]` (this type does not have a fixed size) + = note: target type: `[u8; L * 2]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:89:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; L * 2]` (this type does not have a fixed size) + = note: target type: `[u16; L]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:96:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; L]` (this type does not have a fixed size) + = note: target type: `[[u8; 1]; L]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:105:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[u32; 2 * H]; W + W]` (size can vary because of [u32; 2 * H]) + = note: target type: `[[u32; W + W]; 2 * H]` (size can vary because of [u32; W + W]) + error[E0308]: mismatched types --> $DIR/transmute-fail.rs:12:53 | @@ -46,7 +127,7 @@ error[E0308]: mismatched types LL | fn bar(v: [[u32; H]; W]) -> [[u32; W]; H] { | ^ expected `usize`, found `bool` -error: aborting due to 6 previous errors +error: aborting due to 15 previous errors Some errors have detailed explanations: E0308, E0512. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/transmute.rs b/tests/ui/const-generics/transmute.rs index 245fcf5670e..e8ab8637932 100644 --- a/tests/ui/const-generics/transmute.rs +++ b/tests/ui/const-generics/transmute.rs @@ -3,81 +3,12 @@ #![feature(transmute_generic_consts)] #![allow(incomplete_features)] -fn transpose(v: [[u32;H]; W]) -> [[u32; W]; H] { - unsafe { - std::mem::transmute(v) - } -} - fn ident(v: [[u32; H]; W]) -> [[u32; H]; W] { unsafe { std::mem::transmute(v) } } -fn flatten(v: [[u32; H]; W]) -> [u32; W * H] { - unsafe { - std::mem::transmute(v) - } -} - -fn coagulate(v: [u32; H*W]) -> [[u32; W];H] { - unsafe { - std::mem::transmute(v) - } -} - -fn flatten_3d( - v: [[[u32; D]; H]; W] -) -> [u32; D * W * H] { - unsafe { - std::mem::transmute(v) - } -} - -fn flatten_somewhat( - v: [[[u32; D]; H]; W] -) -> [[u32; D * W]; H] { - unsafe { - std::mem::transmute(v) - } -} - -fn known_size(v: [u16; L]) -> [u8; L * 2] { - unsafe { - std::mem::transmute(v) - } -} - -fn condense_bytes(v: [u8; L * 2]) -> [u16; L] { - unsafe { - std::mem::transmute(v) - } -} - -fn singleton_each(v: [u8; L]) -> [[u8;1]; L] { - unsafe { - std::mem::transmute(v) - } -} - -fn transpose_with_const( - v: [[u32; 2 * H]; W + W] -) -> [[u32; W + W]; 2 * H] { - unsafe { - std::mem::transmute(v) - } -} - fn main() { - let _ = transpose([[0; 8]; 16]); - let _ = transpose_with_const::<8,4>([[0; 8]; 16]); let _ = ident([[0; 8]; 16]); - let _ = flatten([[0; 13]; 5]); - let _: [[_; 5]; 13] = coagulate([0; 65]); - let _ = flatten_3d([[[0; 3]; 13]; 5]); - let _ = flatten_somewhat([[[0; 3]; 13]; 5]); - let _ = known_size([16; 13]); - let _: [u16; 5] = condense_bytes([16u8; 10]); - let _ = singleton_each([16; 10]); } -- cgit 1.4.1-3-g733a5 From 91aae58568ffc2236662b14866deef7e63e4f8e9 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Mar 2024 22:27:30 +1100 Subject: coverage: Clean up marker statements that aren't needed later Some of the marker statements used by coverage are added during MIR building for use by the InstrumentCoverage pass (during analysis), and are not needed afterwards. --- .../rustc_codegen_llvm/src/coverageinfo/mod.rs | 10 +--- .../rustc_const_eval/src/transform/validate.rs | 13 ++++- compiler/rustc_middle/src/mir/coverage.rs | 5 +- compiler/rustc_middle/src/mir/syntax.rs | 1 + .../src/cleanup_post_borrowck.rs | 13 ++++- ..._coverage_cleanup.main.CleanupPostBorrowck.diff | 56 ++++++++++++++++++++++ ...t_coverage_cleanup.main.InstrumentCoverage.diff | 53 ++++++++++++++++++++ tests/mir-opt/instrument_coverage_cleanup.rs | 22 +++++++++ 8 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff create mode 100644 tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff create mode 100644 tests/mir-opt/instrument_coverage_cleanup.rs (limited to 'compiler/rustc_middle/src') diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 133084b7c12..54f4bc06340 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -85,14 +85,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let bx = self; - match coverage.kind { - // Marker statements have no effect during codegen, - // so return early and don't create `func_coverage`. - CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => return, - // Match exhaustively to ensure that newly-added kinds are classified correctly. - CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {} - } - let Some(function_coverage_info) = bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref() else { @@ -109,7 +101,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let Coverage { kind } = coverage; match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( - "unexpected marker statement {kind:?} should have caused an early return" + "marker statement {kind:?} should have been removed by CleanupPostBorrowck" ), CoverageKind::CounterIncrement { id } => { func_coverage.mark_counter_id_seen(id); diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 68270dab284..4bc49f90607 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_infer::traits::Reveal; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -345,11 +346,21 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } + StatementKind::Coverage(coverage) => { + let kind = &coverage.kind; + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) + && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind + { + self.fail( + location, + format!("{kind:?} should have been removed after analysis"), + ); + } + } StatementKind::Assign(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Intrinsic(_) - | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) | StatementKind::Nop => {} diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 645a417c322..588aa1f40d7 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -88,14 +88,13 @@ pub enum CoverageKind { /// Marks a span that might otherwise not be represented in MIR, so that /// coverage instrumentation can associate it with its enclosing block/BCB. /// - /// Only used by the `InstrumentCoverage` pass, and has no effect during - /// codegen. + /// Should be erased before codegen (at some point after `InstrumentCoverage`). SpanMarker, /// Marks its enclosing basic block with an ID that can be referred to by /// side data in [`BranchInfo`]. /// - /// Has no effect during codegen. + /// Should be erased before codegen (at some point after `InstrumentCoverage`). BlockMarker { id: BlockMarkerId }, /// Marks the point in MIR control flow represented by a coverage counter. diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0ab797134c0..752f5845afb 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -124,6 +124,7 @@ pub enum AnalysisPhase { /// * [`TerminatorKind::FalseEdge`] /// * [`StatementKind::FakeRead`] /// * [`StatementKind::AscribeUserType`] + /// * [`StatementKind::Coverage`] with [`CoverageKind::BlockMarker`] or [`CoverageKind::SpanMarker`] /// * [`Rvalue::Ref`] with `BorrowKind::Fake` /// /// Furthermore, `Deref` projections must be the first projection within any place (if they diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 5b4bc4fa134..aaf2035fc21 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -5,15 +5,20 @@ //! - [`AscribeUserType`] //! - [`FakeRead`] //! - [`Assign`] statements with a [`Fake`] borrow +//! - [`Coverage`] statements of kind [`BlockMarker`] or [`SpanMarker`] //! //! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType //! [`Assign`]: rustc_middle::mir::StatementKind::Assign //! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead //! [`Nop`]: rustc_middle::mir::StatementKind::Nop //! [`Fake`]: rustc_middle::mir::BorrowKind::Fake +//! [`Coverage`]: rustc_middle::mir::StatementKind::Coverage +//! [`BlockMarker`]: rustc_middle::mir::coverage::CoverageKind::BlockMarker +//! [`SpanMarker`]: rustc_middle::mir::coverage::CoverageKind::SpanMarker use crate::MirPass; -use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::mir::coverage::CoverageKind; +use rustc_middle::mir::{Body, BorrowKind, Coverage, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; pub struct CleanupPostBorrowck; @@ -25,6 +30,12 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { match statement.kind { StatementKind::AscribeUserType(..) | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _))) + | StatementKind::Coverage(box Coverage { + // These kinds of coverage statements are markers inserted during + // MIR building, and are not needed after InstrumentCoverage. + kind: CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, + .. + }) | StatementKind::FakeRead(..) => statement.make_nop(), _ => (), } diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff new file mode 100644 index 00000000000..ff65ca77039 --- /dev/null +++ b/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff @@ -0,0 +1,56 @@ +- // MIR for `main` before CleanupPostBorrowck ++ // MIR for `main` after CleanupPostBorrowck + + fn main() -> () { + let mut _0: (); + let mut _1: bool; + + coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0) + + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; + coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; + coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36; + coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39; + coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40; + coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2; + coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36; + + bb0: { + Coverage::CounterIncrement(0); +- Coverage::SpanMarker; ++ nop; + StorageLive(_1); + _1 = std::hint::black_box::(const true) -> [return: bb1, unwind: bb5]; + } + + bb1: { + switchInt(move _1) -> [0: bb3, otherwise: bb2]; + } + + bb2: { + Coverage::CounterIncrement(1); +- Coverage::BlockMarker(1); ++ nop; + _0 = const (); + goto -> bb4; + } + + bb3: { + Coverage::ExpressionUsed(0); +- Coverage::BlockMarker(0); ++ nop; + _0 = const (); + goto -> bb4; + } + + bb4: { + Coverage::ExpressionUsed(1); + StorageDead(_1); + return; + } + + bb5 (cleanup): { + resume; + } + } + diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff new file mode 100644 index 00000000000..8757559149a --- /dev/null +++ b/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff @@ -0,0 +1,53 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); + let mut _1: bool; + + coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0) + ++ coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; ++ coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; ++ coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36; ++ coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39; ++ coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40; ++ coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2; ++ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36; ++ + bb0: { ++ Coverage::CounterIncrement(0); + Coverage::SpanMarker; + StorageLive(_1); + _1 = std::hint::black_box::(const true) -> [return: bb1, unwind: bb5]; + } + + bb1: { + switchInt(move _1) -> [0: bb3, otherwise: bb2]; + } + + bb2: { ++ Coverage::CounterIncrement(1); + Coverage::BlockMarker(1); + _0 = const (); + goto -> bb4; + } + + bb3: { ++ Coverage::ExpressionUsed(0); + Coverage::BlockMarker(0); + _0 = const (); + goto -> bb4; + } + + bb4: { ++ Coverage::ExpressionUsed(1); + StorageDead(_1); + return; + } + + bb5 (cleanup): { + resume; + } + } + diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/instrument_coverage_cleanup.rs new file mode 100644 index 00000000000..8a2fd67139b --- /dev/null +++ b/tests/mir-opt/instrument_coverage_cleanup.rs @@ -0,0 +1,22 @@ +// Test that CleanupPostBorrowck cleans up the marker statements that are +// inserted during MIR building (after InstrumentCoverage is done with them), +// but leaves the statements that were added by InstrumentCoverage. +// +// Removed statement kinds: BlockMarker, SpanMarker +// Retained statement kinds: CounterIncrement, ExpressionUsed + +//@ unit-test: InstrumentCoverage +//@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime +//@ compile-flags: --remap-path-prefix={{src-base}}=/the/src + +// EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff +// EMIT_MIR instrument_coverage_cleanup.main.CleanupPostBorrowck.diff +fn main() { + if !core::hint::black_box(true) {} +} + +// CHECK-NOT: Coverage::BlockMarker +// CHECK-NOT: Coverage::SpanMarker +// CHECK: Coverage::CounterIncrement +// CHECK-NOT: Coverage::BlockMarker +// CHECK-NOT: Coverage::SpanMarker -- cgit 1.4.1-3-g733a5