diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2ef6fa53f4edd..cd78423d7100c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4076,11 +4076,17 @@ mod size_asserts { use rustc_data_structures::static_assert_size; use super::*; - // tidy-alphabetical-start static_assert_size!(Block<'_>, 48); static_assert_size!(Body<'_>, 24); + #[cfg(bootstrap)] static_assert_size!(Expr<'_>, 64); + #[cfg(not(bootstrap))] + static_assert_size!(Expr<'_>, 56); + #[cfg(bootstrap)] static_assert_size!(ExprKind<'_>, 48); + #[cfg(not(bootstrap))] + // tidy-alphabetical-start + static_assert_size!(ExprKind<'_>, 40); static_assert_size!(FnDecl<'_>, 40); static_assert_size!(ForeignItem<'_>, 88); static_assert_size!(ForeignItemKind<'_>, 56); diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 8a7005ac32893..b0dbd40c6b530 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -1,5 +1,6 @@ use hir::HirId; use rustc_abi::Primitive::Pointer; +use rustc_abi::Size; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; @@ -88,8 +89,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn size_to_bits(size: Size) -> u128 { + let Some(bits) = u128::from(size.bytes()).checked_mul(8) else { + // `u128` should definitely be able to hold the size of different architectures + // larger sizes should be reported as error `are too big for the current architecture` + // otherwise we have a bug somewhere + bug!("{:?} overflow for u128", size) + }; + + bits + } + // Try to display a sensible error with as much information as possible. let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk { + Ok(SizeSkeleton::Pointer { tail, known_size: Some(size), .. }) => { + format!("{} bits, pointer to `{tail}`", size_to_bits(size)) + } Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), Ok(SizeSkeleton::Known(size, _)) => { if let Some(v) = u128::from(size.bytes()).checked_mul(8) { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6c12b691c26c0..d8ba0e4070bf3 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -7,6 +7,7 @@ use rustc_abi::{ Abi, AddressSpace, Align, FieldsShape, HasDataLayout, Integer, LayoutCalculator, LayoutS, PointeeInfo, PointerKind, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, Variants, }; +use rustc_ast::Mutability; use rustc_error_messages::DiagMessage; use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, @@ -326,6 +327,8 @@ pub enum SizeSkeleton<'tcx> { Pointer { /// If true, this pointer is never null. non_zero: bool, + /// Available if the width of the pointer is known, i.e. whether it's 1 or 2 usizes + known_size: Option, /// The type which determines the unsized metadata, if any, /// of this pointer. Either a type parameter or a projection /// depending on one, with regions erased. @@ -384,7 +387,23 @@ impl<'tcx> SizeSkeleton<'tcx> { match tail.kind() { ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => { debug_assert!(tail.has_non_region_param()); - Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) + Ok(SizeSkeleton::Pointer { + non_zero, + known_size: None, + tail: tcx.erase_regions(tail), + }) + } + ty::Slice(_) => { + debug_assert!(tail.has_non_region_param()); + // Assumption: all slice pointers have the same size. At most they differ in niches or or ptr/len ordering + let simple_slice = + Ty::new_ptr(tcx, Ty::new_slice(tcx, tcx.types.unit), Mutability::Not); + let size = tcx.layout_of(param_env.and(simple_slice)).unwrap().size; + Ok(SizeSkeleton::Pointer { + non_zero, + known_size: Some(size), + tail: tcx.erase_regions(tail), + }) } ty::Error(guar) => { // Fixes ICE #124031 @@ -462,7 +481,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let v0 = zero_or_ptr_variant(0)?; // Newtype. if def.variants().len() == 1 { - if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { + if let Some(SizeSkeleton::Pointer { non_zero, known_size, tail }) = v0 { return Ok(SizeSkeleton::Pointer { non_zero: non_zero || match tcx.layout_scalar_valid_range(def.did()) { @@ -472,6 +491,7 @@ impl<'tcx> SizeSkeleton<'tcx> { } _ => false, }, + known_size, tail, }); } else { @@ -482,9 +502,9 @@ impl<'tcx> SizeSkeleton<'tcx> { let v1 = zero_or_ptr_variant(1)?; // Nullable pointer enum optimization. match (v0, v1) { - (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) - | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { - Ok(SizeSkeleton::Pointer { non_zero: false, tail }) + (Some(SizeSkeleton::Pointer { non_zero: true, known_size, tail }), None) + | (None, Some(SizeSkeleton::Pointer { non_zero: true, known_size, tail })) => { + Ok(SizeSkeleton::Pointer { non_zero: false, known_size, tail }) } _ => Err(err), } @@ -505,7 +525,10 @@ impl<'tcx> SizeSkeleton<'tcx> { pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool { match (self, other) { - (SizeSkeleton::Known(a, _), SizeSkeleton::Known(b, _)) => a == b, + ( + SizeSkeleton::Known(a, _) | SizeSkeleton::Pointer { known_size: Some(a), .. }, + SizeSkeleton::Known(b, _) | SizeSkeleton::Pointer { known_size: Some(b), .. }, + ) => a == b, (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { a == b } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index afdfa2e80c1f6..92195ca36d245 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1,5 +1,6 @@ use std::fmt::Debug; -use std::iter; +use std::ops::ControlFlow; +use std::{cmp, iter}; use hir::def_id::DefId; use rustc_abi::Integer::{I8, I32}; @@ -18,12 +19,14 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, AdtDef, CoroutineArgsExt, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, + self, AdtDef, CoroutineArgsExt, EarlyBinder, GenericArgsRef, ParamEnv, Ty, TyCtxt, + TypeVisitableExt, }; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::{FIRST_VARIANT, FieldIdx, Layout, VariantIdx}; +use rustc_type_ir::DynKind; use tracing::{debug, instrument, trace}; use {rustc_abi as abi, rustc_hir as hir}; @@ -161,7 +164,7 @@ fn layout_of_uncached<'tcx>( }; debug_assert!(!ty.has_non_region_infer()); - Ok(match *ty.kind() { + let layout = match *ty.kind() { ty::Pat(ty, pat) => { let layout = cx.layout_of(ty)?.layout; let mut layout = LayoutS::clone(&layout.0); @@ -198,7 +201,6 @@ fn layout_of_uncached<'tcx>( } } } - // Basic scalars. ty::Bool => tcx.mk_layout(LayoutS::scalar(cx, Scalar::Initialized { value: Int(I8, false), @@ -269,10 +271,32 @@ fn layout_of_uncached<'tcx>( return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); } - let Abi::Scalar(metadata) = metadata_layout.abi else { + let Abi::Scalar(mut metadata) = metadata_layout.abi else { return Err(error(cx, LayoutError::Unknown(pointee))); }; + if !ty.is_unsafe_ptr() && metadata_ty == tcx.types.usize { + let tail = tcx.struct_tail_for_codegen(pointee, param_env); + // // eprintln!("usize-meta {:?} {}", pointee, pointee_zst); + match tail.kind() { + ty::Slice(element) => match ty_is_non_zst(*element, param_env, tcx) { + NonZst::True => { + metadata.valid_range_mut().end = + dl.ptr_sized_integer().signed_max() as u128 + } + NonZst::Unknown => return Err(error(cx, LayoutError::Unknown(ty))), + _ => {} + }, + ty::Str => { + metadata.valid_range_mut().end = + dl.ptr_sized_integer().signed_max() as u128; + } + _ => { + eprint!("unexpected tail {:?}", tail); + } + } + } + metadata } else { let unsized_part = tcx.struct_tail_for_codegen(pointee, param_env); @@ -281,7 +305,28 @@ fn layout_of_uncached<'tcx>( ty::Foreign(..) => { return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); } - ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), + ty::Slice(element) => { + let mut metadata = scalar_unit(Int(dl.ptr_sized_integer(), false)); + if !ty.is_unsafe_ptr() { + match ty_is_non_zst(*element, param_env, tcx) { + NonZst::True => { + metadata.valid_range_mut().end = + dl.ptr_sized_integer().signed_max() as u128 + } + NonZst::Unknown => return Err(error(cx, LayoutError::Unknown(ty))), + _ => {} + } + } + metadata + } + ty::Str => { + let mut metadata = scalar_unit(Int(dl.ptr_sized_integer(), false)); + if !ty.is_unsafe_ptr() { + metadata.valid_range_mut().end = + dl.ptr_sized_integer().signed_max() as u128; + } + metadata + } ty::Dynamic(..) => { let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); vtable.valid_range_mut().start = 1; @@ -673,7 +718,157 @@ fn layout_of_uncached<'tcx>( ty::Placeholder(..) | ty::Param(_) => { return Err(error(cx, LayoutError::Unknown(ty))); } - }) + }; + + #[cfg(debug_assertions)] + if layout.is_sized() && !layout.abi.is_uninhabited() { + match (ty_is_non_zst(ty, param_env, tcx), layout.is_zst()) { + (NonZst::Unknown, _) => { + bug!("ZSTness should not be unknown at this point {:?} {:?}", ty, layout) + } + (n @ (NonZst::False | NonZst::Uninhabited), false) => { + bug!("{:?} is not a ZST but ty_is_non_zst() thinks it is NonZst::{:?}", ty, n) + } + (NonZst::True, true) => bug!("{:?} is a ZST but ty_is_non_zst() thinks it isn't", ty), + _ => {} + } + } + + Ok(layout) +} + +fn ty_is_non_zst<'tcx>(ty: Ty<'tcx>, param_env: ParamEnv<'tcx>, tcx: TyCtxt<'tcx>) -> NonZst { + fn fold_fields<'tcx>( + mut it: impl Iterator>, + param_env: ParamEnv<'tcx>, + tcx: TyCtxt<'tcx>, + ) -> NonZst { + let (ControlFlow::Break(res) | ControlFlow::Continue(res)) = + it.try_fold(NonZst::False, |acc, ty| { + if acc == NonZst::True { + return ControlFlow::Break(acc); + } + + ControlFlow::Continue(cmp::max(acc, ty_is_non_zst(ty, param_env, tcx))) + }); + + res + } + + match ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnPtr(_, _) + | ty::RawPtr(..) + | ty::Dynamic(_, _, DynKind::DynStar) + | ty::Char + | ty::Ref(..) => NonZst::True, + + ty::Pat(ty, _) => ty_is_non_zst(*ty, param_env, tcx), + ty::Closure(_, args) => fold_fields(args.as_closure().upvar_tys().iter(), param_env, tcx), + ty::Coroutine(_, _) => NonZst::True, + ty::CoroutineClosure(_, args) => { + fold_fields(args.as_coroutine_closure().upvar_tys().iter(), param_env, tcx) + } + ty::Array(ty, len) => { + let len = if len.has_aliases() { + tcx.normalize_erasing_regions(param_env, *len) + } else { + *len + }; + + if let Some(len) = len.try_to_target_usize(tcx) { + if len == 0 { + return NonZst::False; + } + let element_zst = ty_is_non_zst(*ty, param_env, tcx); + if element_zst != NonZst::Unknown { + return element_zst; + } + } + NonZst::Unknown + } + ty::Tuple(tys) => fold_fields(tys.iter(), param_env, tcx), + ty::Adt(def, args) => { + if ty.is_enum() { + // repr(C) enums can never be ZSTs or uninhabited. + // They must have at least one variant and even if the variant has a payload that is uninhabited, + // the tag is still there. + if def.repr().c() { + return NonZst::True; + } + + if def.variants().len() == 0 { + return NonZst::Uninhabited; + } + // An enum is !ZST if + // * it has a repr(int) and at least one non-uninhabited variant + // * it has at least one variant with a !ZST payload + // * it has multiple variants that are not uninhabited + + let min_empty_variants = if def.repr().inhibit_enum_layout_opt() { 1 } else { 2 }; + + // first check without recursing + let simple_variants = def.variants().iter().filter(|v| v.fields.len() == 0).count(); + if simple_variants >= min_empty_variants { + return NonZst::True; + } + + let mut inhabited_zst_variants = 0; + let mut unknown = false; + + for variant in def.variants().iter().filter(|v| v.fields.len() != 0) { + let variant_sized = + fold_fields(variant.fields.iter().map(|f| f.ty(tcx, args)), param_env, tcx); + + match variant_sized { + // enum E { A(!, u32) } counts as !ZST for our purposes + NonZst::True => return NonZst::True, + NonZst::False => inhabited_zst_variants += 1, + NonZst::Unknown => unknown = true, + NonZst::Uninhabited => {} + } + } + + if simple_variants + inhabited_zst_variants >= min_empty_variants { + return NonZst::True; + } + if unknown { + return NonZst::Unknown; + } + if simple_variants + inhabited_zst_variants == 0 { + return NonZst::Uninhabited; + } + + NonZst::False + } else { + fold_fields(def.all_fields().map(|f| f.ty(tcx, args)), param_env, tcx) + } + } + ty::FnDef(..) => NonZst::False, + ty::Never => NonZst::Uninhabited, + ty::Param(..) => NonZst::Unknown, + ty::Str => NonZst::True, + // treat unsized types as potentially-ZST + ty::Dynamic(..) | ty::Slice(..) => NonZst::False, + ty::Alias(..) => match tcx.try_normalize_erasing_regions(param_env, ty) { + Ok(ty) if !matches!(ty.kind(), ty::Alias(..)) => ty_is_non_zst(ty, param_env, tcx), + _ => NonZst::Unknown, + }, + ty::Error(_) => NonZst::Unknown, + _ => bug!("is_non_zst not implemented for this kind {:?}", ty), + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] +enum NonZst { + False, + Uninhabited, + Unknown, + True, } /// Overlap eligibility and variant assignment for each CoroutineSavedLocal. diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index bcc5bf4d65bb4..1a9f782cb2a73 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -94,14 +94,12 @@ macro_rules! impl_is_zero_tuples { impl_is_zero_tuples!(A, B, C, D, E, F, G, H); -// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. -// For fat pointers, the bytes that would be the pointer metadata in the `Some` -// variant are padding in the `None` variant, so ignoring them and -// zero-initializing instead is ok. +// For thin pointers `Option<&T>` and `Option>` are guaranteed to represent +// `None` as null. // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of // `SpecFromElem`. -unsafe impl IsZero for Option<&T> { +unsafe impl IsZero for Option<&T> { #[inline] fn is_zero(&self) -> bool { self.is_none() diff --git a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs index 4e363dbf81e08..725d473a5d36f 100644 --- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs +++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs @@ -1,7 +1,13 @@ use std::mem; -// If this is `None`, the metadata becomes padding. -type T = Option<&'static str>; +#[repr(C)] +struct RefAndLen { + ptr: &'static u8, + len: usize, +} + +// If this is `None`, the len becomes padding. +type T = Option; fn main() { unsafe { diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs index bf9f405192b69..623fba5362ec0 100644 --- a/tests/codegen/function-arguments.rs +++ b/tests/codegen/function-arguments.rs @@ -1,4 +1,5 @@ //@ compile-flags: -O -C no-prepopulate-passes +//@ min-llvm-version: 19 #![crate_type = "lib"] #![feature(dyn_star)] #![feature(allocator_api)] @@ -193,17 +194,17 @@ pub fn struct_return() -> S { #[no_mangle] pub fn helper(_: usize) {} -// CHECK: @slice(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1) +// CHECK: @slice(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn slice(_: &[u8]) {} -// CHECK: @mutable_slice(ptr noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef %_1.1) +// CHECK: @mutable_slice(ptr noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn mutable_slice(_: &mut [u8]) {} -// CHECK: @unsafe_slice(ptr noundef nonnull align 2 %_1.0, [[USIZE]] noundef %_1.1) +// CHECK: @unsafe_slice(ptr noundef nonnull align 2 %_1.0, [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %_1.1) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] pub fn unsafe_slice(_: &[UnsafeInner]) {} @@ -212,7 +213,7 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {} #[no_mangle] pub fn raw_slice(_: *const [u8]) {} -// CHECK: @str(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1) +// CHECK: @str(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn str(_: &[u8]) {} @@ -244,7 +245,7 @@ pub fn trait_option(x: Option>) -> Option &[u16] { x diff --git a/tests/codegen/intrinsics/transmute.rs b/tests/codegen/intrinsics/transmute.rs index 8c8e975d327a2..7ef9e81aaed7f 100644 --- a/tests/codegen/intrinsics/transmute.rs +++ b/tests/codegen/intrinsics/transmute.rs @@ -370,9 +370,11 @@ pub unsafe fn check_issue_110005(x: (usize, bool)) -> Option> { #[no_mangle] pub unsafe fn check_pair_to_dst_ref<'a>(x: (usize, usize)) -> &'a [u8] { // CHECK: %_0.0 = getelementptr i8, ptr null, i64 %x.0 - // CHECK: %0 = insertvalue { ptr, i64 } poison, ptr %_0.0, 0 - // CHECK: %1 = insertvalue { ptr, i64 } %0, i64 %x.1, 1 - // CHECK: ret { ptr, i64 } %1 + // CHECK: %0 = icmp ule i64 %x.1, 9223372036854775807 + // CHECK: call void @llvm.assume(i1 %0) + // CHECK: %1 = insertvalue { ptr, i64 } poison, ptr %_0.0, 0 + // CHECK: %2 = insertvalue { ptr, i64 } %1, i64 %x.1, 1 + // CHECK: ret { ptr, i64 } %2 transmute(x) } diff --git a/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs b/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs index aee2edd8dfad2..5716daacad35c 100644 --- a/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs +++ b/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs @@ -5,7 +5,8 @@ // CHECK-NEXT: {{.*}}: // CHECK-NEXT: icmp ugt // CHECK-NEXT: getelementptr -// CHECK-NEXT: select i1 {{.+}} null +// CHECK-NEXT: select i1 {{.+}} +// CHECK-NEXT: select i1 {{.+}} // CHECK-NEXT: insertvalue // CHECK-NEXT: insertvalue // CHECK-NEXT: ret diff --git a/tests/codegen/overaligned-constant.rs b/tests/codegen/overaligned-constant.rs index 7cd8d19c21135..3feb05d365798 100644 --- a/tests/codegen/overaligned-constant.rs +++ b/tests/codegen/overaligned-constant.rs @@ -7,7 +7,8 @@ struct S(i32); -struct SmallStruct(f32, Option, &'static [f32]); +#[repr(C)] +struct SmallStruct(Option, f32, &'static [f32]); // CHECK: @0 = private unnamed_addr constant // CHECK-SAME: , align 8 @@ -17,14 +18,14 @@ pub fn overaligned_constant() { // CHECK-LABEL: @overaligned_constant // CHECK: [[full:%_.*]] = alloca [32 x i8], align 8 // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[full]], ptr align 8 @0, i64 32, i1 false) - // CHECK: %b.0 = load i32, ptr @0, align 4 - // CHECK: %b.1 = load i32, ptr getelementptr inbounds ({{.*}}), align 4 + // CHECK: %a.0 = load i32, ptr @0, align 4 + // CHECK: %a.1 = load i32, ptr getelementptr inbounds ({{.*}}), align 4 let mut s = S(1); s.0 = 3; // SMALL_VAL corresponds to a MIR allocation with alignment 8. - const SMALL_VAL: SmallStruct = SmallStruct(4., Some(S(1)), &[]); + const SMALL_VAL: SmallStruct = SmallStruct(Some(S(1)), 4., &[]); // In pre-codegen MIR: // `a` is a scalar 4. diff --git a/tests/codegen/range-attribute.rs b/tests/codegen/range-attribute.rs index bb19bec0fb932..4a8cdf89f7b7e 100644 --- a/tests/codegen/range-attribute.rs +++ b/tests/codegen/range-attribute.rs @@ -61,7 +61,7 @@ pub fn enum2_value(x: Enum2) -> Enum2 { x } -// CHECK: noundef [[USIZE]] @takes_slice(ptr noalias noundef nonnull readonly align 4 %x.0, [[USIZE]] noundef %x.1) +// CHECK: noundef [[USIZE]] @takes_slice(ptr noalias noundef nonnull readonly align 4 %x.0, [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %x.1) #[no_mangle] pub fn takes_slice(x: &[i32]) -> usize { x.len() diff --git a/tests/codegen/slice-as_chunks.rs b/tests/codegen/slice-as_chunks.rs index 631d18d780951..c2a2c98c6ee46 100644 --- a/tests/codegen/slice-as_chunks.rs +++ b/tests/codegen/slice-as_chunks.rs @@ -1,4 +1,5 @@ //@ compile-flags: -O +//@ min-llvm-version: 19 //@ only-64bit (because the LLVM type of i64 for usize shows up) #![crate_type = "lib"] @@ -20,7 +21,7 @@ pub fn chunks4(x: &[u8]) -> &[[u8; 4]] { // CHECK-LABEL: @chunks4_with_remainder #[no_mangle] pub fn chunks4_with_remainder(x: &[u8]) -> (&[[u8; 4]], &[u8]) { - // CHECK-DAG: and i64 %x.1, -4 + // CHECK-DAG: and i64 %x.1, 9223372036854775804 // CHECK-DAG: and i64 %x.1, 3 // CHECK-DAG: lshr // CHECK-NOT: mul diff --git a/tests/codegen/slice-indexing.rs b/tests/codegen/slice-indexing.rs index 3d284148db22e..92b411be5221a 100644 --- a/tests/codegen/slice-indexing.rs +++ b/tests/codegen/slice-indexing.rs @@ -36,7 +36,7 @@ pub unsafe fn get_unchecked_mut_by_range(x: &mut [i32], r: Range) -> &mut // CHECK-LABEL: @str_index_by_range( #[no_mangle] pub fn str_index_by_range(x: &str, r: Range) -> &str { - // CHECK: sub nuw i64 + // CHECK: sub nuw nsw i64 &x[r] } @@ -50,7 +50,7 @@ pub unsafe fn str_get_unchecked_by_range(x: &str, r: Range) -> &str { // CHECK-LABEL: @str_index_mut_by_range( #[no_mangle] pub fn str_index_mut_by_range(x: &mut str, r: Range) -> &mut str { - // CHECK: sub nuw i64 + // CHECK: sub nuw nsw i64 &mut x[r] } diff --git a/tests/codegen/slice-iter-nonnull.rs b/tests/codegen/slice-iter-nonnull.rs index eda807d36822d..2ea1dca0255d5 100644 --- a/tests/codegen/slice-iter-nonnull.rs +++ b/tests/codegen/slice-iter-nonnull.rs @@ -51,7 +51,7 @@ pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&' // attribute is there, and confirms adding the assume back doesn't do anything. // CHECK-LABEL: @slice_iter_new -// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1) +// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}} 0, -{{[0-9]+}}) %slice.1) #[no_mangle] pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> { // CHECK-NOT: slice @@ -66,7 +66,7 @@ pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> { } // CHECK-LABEL: @slice_iter_mut_new -// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1) +// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef range({{.+}} 0, -{{[0-9]+}}) %slice.1) #[no_mangle] pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> { // CHECK-NOT: slice diff --git a/tests/codegen/slice-len.rs b/tests/codegen/slice-len.rs new file mode 100644 index 0000000000000..75bb53410e6bd --- /dev/null +++ b/tests/codegen/slice-len.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -O +#![crate_type = "lib"] + +// check that slice.len() has range metadata + +// CHECK-LABEL: @slice_len +#[no_mangle] +pub fn slice_len(slice: &&[i32]) -> usize { + // CHECK: load {{i64|i32}} + // CHECK-SAME: !range + slice.len() + // CHECK: ret +} diff --git a/tests/codegen/slice-ref-equality.rs b/tests/codegen/slice-ref-equality.rs index 1153d7817b278..9896673708099 100644 --- a/tests/codegen/slice-ref-equality.rs +++ b/tests/codegen/slice-ref-equality.rs @@ -1,4 +1,5 @@ //@ compile-flags: -O -Zmerge-functions=disabled +//@ min-llvm-version: 19 #![crate_type = "lib"] use std::num::NonZero; @@ -42,20 +43,20 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool { // equality for non-byte types also just emit a `bcmp`, not a loop. // CHECK-LABEL: @eq_slice_of_nested_u8( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %x.1 +// CHECK-SAME: [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %y.1 #[no_mangle] fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool { // CHECK: icmp eq [[USIZE]] %x.1, %y.1 - // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] {{%x.1|%y.1}}, 3 + // CHECK: %[[BYTES:.+]] = mul nuw nsw [[USIZE]] {{%x.1|%y.1}}, 3 // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) x == y } // CHECK-LABEL: @eq_slice_of_i32( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %x.1 +// CHECK-SAME: [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %y.1 #[no_mangle] fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool { // CHECK: icmp eq [[USIZE]] %x.1, %y.1 @@ -66,8 +67,8 @@ fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool { } // CHECK-LABEL: @eq_slice_of_nonzero( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %x.1 +// CHECK-SAME: [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %y.1 #[no_mangle] fn eq_slice_of_nonzero(x: &[NonZero], y: &[NonZero]) -> bool { // CHECK: icmp eq [[USIZE]] %x.1, %y.1 @@ -78,12 +79,12 @@ fn eq_slice_of_nonzero(x: &[NonZero], y: &[NonZero]) -> bool { } // CHECK-LABEL: @eq_slice_of_option_of_nonzero( -// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1 -// CHECK-SAME: [[USIZE]] noundef %y.1 +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %x.1 +// CHECK-SAME: [[USIZE]] noundef range([[USIZE]] 0, -{{[0-9]+}}) %y.1 #[no_mangle] fn eq_slice_of_option_of_nonzero(x: &[Option>], y: &[Option>]) -> bool { // CHECK: icmp eq [[USIZE]] %x.1, %y.1 - // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 1 + // CHECK: %[[BYTES:.+]] = shl nuw nsw [[USIZE]] {{%x.1|%y.1}}, 1 // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) x == y diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 1e462210cba21..65f4224cfef08 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -60,7 +60,7 @@ pub type TypeAlias = X; pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); // Regression test for the rustdoc equivalent of #85103. -//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.' +//@ hasraw type_layout/type.Edges.html 'Unable to compute type layout' pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; //@ !hasraw type_layout/trait.MyTrait.html 'Size: ' diff --git a/tests/ui/issues/issue-28625.stderr b/tests/ui/issues/issue-28625.stderr index 3600622a45424..dc7e87c6c2968 100644 --- a/tests/ui/issues/issue-28625.stderr +++ b/tests/ui/issues/issue-28625.stderr @@ -5,7 +5,7 @@ LL | unsafe { std::mem::transmute(a) } | ^^^^^^^^^^^^^^^^^^^ | = note: source type: `&ArrayPeano` (N bits) - = note: target type: `&[T]` (N bits) + = note: target type: `&[T]` (N bits, pointer to `[T]`) error: aborting due to 1 previous error diff --git a/tests/ui/stats/hir-stats.rs b/tests/ui/stats/hir-stats.rs index 7c5da8cf554b6..4000da787ea14 100644 --- a/tests/ui/stats/hir-stats.rs +++ b/tests/ui/stats/hir-stats.rs @@ -3,6 +3,7 @@ //@ only-x86_64 // layout randomization affects the hir stat output //@ needs-deterministic-layouts +//@ ignore-stage1 // Type layouts sometimes change. When that happens, until the next bootstrap // bump occurs, stage1 and stage2 will give different outputs for this test. diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index b27f769ba34f4..df40a8be06de5 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -121,7 +121,7 @@ hir-stats ---------------------------------------------------------------- hir-stats ForeignItemRef 24 ( 0.3%) 1 24 hir-stats Lifetime 24 ( 0.3%) 1 24 hir-stats Mod 32 ( 0.4%) 1 32 -hir-stats ExprField 40 ( 0.4%) 1 40 +hir-stats ExprField 40 ( 0.5%) 1 40 hir-stats TraitItemRef 56 ( 0.6%) 2 28 hir-stats GenericArg 64 ( 0.7%) 4 16 hir-stats - Type 16 ( 0.2%) 1 @@ -137,42 +137,42 @@ hir-stats Stmt 96 ( 1.1%) 3 32 hir-stats - Let 32 ( 0.4%) 1 hir-stats - Semi 32 ( 0.4%) 1 hir-stats - Expr 32 ( 0.4%) 1 -hir-stats FnDecl 120 ( 1.3%) 3 40 +hir-stats FnDecl 120 ( 1.4%) 3 40 hir-stats Attribute 128 ( 1.4%) 4 32 hir-stats Variant 144 ( 1.6%) 2 72 hir-stats GenericArgs 144 ( 1.6%) 3 48 -hir-stats GenericBound 192 ( 2.1%) 4 48 -hir-stats - Trait 192 ( 2.1%) 4 -hir-stats WherePredicate 192 ( 2.1%) 3 64 -hir-stats - BoundPredicate 192 ( 2.1%) 3 +hir-stats GenericBound 192 ( 2.2%) 4 48 +hir-stats - Trait 192 ( 2.2%) 4 +hir-stats WherePredicate 192 ( 2.2%) 3 64 +hir-stats - BoundPredicate 192 ( 2.2%) 3 hir-stats Block 288 ( 3.2%) 6 48 -hir-stats GenericParam 360 ( 4.0%) 5 72 -hir-stats Pat 360 ( 4.0%) 5 72 +hir-stats GenericParam 360 ( 4.1%) 5 72 +hir-stats Pat 360 ( 4.1%) 5 72 hir-stats - Wild 72 ( 0.8%) 1 hir-stats - Struct 72 ( 0.8%) 1 hir-stats - Binding 216 ( 2.4%) 3 -hir-stats Generics 560 ( 6.2%) 10 56 -hir-stats Ty 720 ( 8.0%) 15 48 +hir-stats Generics 560 ( 6.3%) 10 56 +hir-stats Expr 672 ( 7.6%) 12 56 +hir-stats - Path 56 ( 0.6%) 1 +hir-stats - Struct 56 ( 0.6%) 1 +hir-stats - Match 56 ( 0.6%) 1 +hir-stats - InlineAsm 56 ( 0.6%) 1 +hir-stats - Lit 112 ( 1.3%) 2 +hir-stats - Block 336 ( 3.8%) 6 +hir-stats Ty 720 ( 8.1%) 15 48 hir-stats - Ptr 48 ( 0.5%) 1 hir-stats - Ref 48 ( 0.5%) 1 hir-stats - Path 624 ( 7.0%) 13 -hir-stats Expr 768 ( 8.6%) 12 64 -hir-stats - Path 64 ( 0.7%) 1 -hir-stats - Struct 64 ( 0.7%) 1 -hir-stats - Match 64 ( 0.7%) 1 -hir-stats - InlineAsm 64 ( 0.7%) 1 -hir-stats - Lit 128 ( 1.4%) 2 -hir-stats - Block 384 ( 4.3%) 6 -hir-stats Item 968 (10.8%) 11 88 +hir-stats Item 968 (10.9%) 11 88 hir-stats - Trait 88 ( 1.0%) 1 hir-stats - Enum 88 ( 1.0%) 1 hir-stats - ExternCrate 88 ( 1.0%) 1 hir-stats - ForeignMod 88 ( 1.0%) 1 hir-stats - Impl 88 ( 1.0%) 1 hir-stats - Fn 176 ( 2.0%) 2 -hir-stats - Use 352 ( 3.9%) 4 -hir-stats Path 1_240 (13.8%) 31 40 -hir-stats PathSegment 1_920 (21.4%) 40 48 +hir-stats - Use 352 ( 4.0%) 4 +hir-stats Path 1_240 (14.0%) 31 40 +hir-stats PathSegment 1_920 (21.7%) 40 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 8_960 +hir-stats Total 8_864 hir-stats diff --git a/tests/ui/transmute/transmute-fat-pointers.stderr b/tests/ui/transmute/transmute-fat-pointers.stderr index e8335fcbed9d0..c89e8e24727a0 100644 --- a/tests/ui/transmute/transmute-fat-pointers.stderr +++ b/tests/ui/transmute/transmute-fat-pointers.stderr @@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | unsafe { transmute(x) } | ^^^^^^^^^ | - = note: source type: `&[T]` (N bits) + = note: source type: `&[T]` (N bits, pointer to `[T]`) = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types