From f4e2f2989b19dfe92a732526f561ad5b3b9018b9 Mon Sep 17 00:00:00 2001 From: Christophe Bronner Date: Fri, 7 Aug 2026 22:11:36 -0400 Subject: [PATCH 1/2] Introduce repr(Swift) Squash of the following 8 commits Added Swift repr and callconv Implemented extern "Swift" and started repr(Swift) Also assert size_of and align_of Adjust ABI to account for Swift small structs Splat Swift structs' padding as well Renames and adjust core::mem primitives Fix build post-merge Correct SizedTypeProperties::SIZE x test --- compiler/rustc_abi/src/canon_abi.rs | 6 +- compiler/rustc_abi/src/layout.rs | 9 +- compiler/rustc_abi/src/layout/coroutine.rs | 1 + compiler/rustc_abi/src/layout/simple.rs | 6 + compiler/rustc_abi/src/lib.rs | 18 ++- .../rustc_ast_passes/src/ast_validation.rs | 2 +- .../rustc_codegen_cranelift/src/abi/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/abi.rs | 2 + compiler/rustc_codegen_llvm/src/abi.rs | 6 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + .../rustc_codegen_ssa/src/mir/intrinsic.rs | 10 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 2 +- compiler/rustc_codegen_ssa/src/size_of_val.rs | 33 +++-- .../src/interpret/eval_context.rs | 5 +- .../src/interpret/intrinsics.rs | 12 +- .../rustc_const_eval/src/interpret/traits.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 5 +- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 9 ++ compiler/rustc_public/src/abi.rs | 1 + .../src/unstable/convert/stable/abi.rs | 3 +- compiler/rustc_span/src/symbol.rs | 5 + compiler/rustc_target/src/callconv/mod.rs | 33 +++++ compiler/rustc_target/src/spec/abi_map.rs | 2 +- compiler/rustc_ty_utils/src/abi.rs | 3 + library/core/src/intrinsics/mod.rs | 29 +++- library/core/src/mem/mod.rs | 131 +++++++++++++++++- library/core/src/ptr/mod.rs | 2 +- library/core/src/slice/cmp.rs | 2 +- ...uginfo-type-name-layout-ice-94961-1.stderr | 2 +- ...uginfo-type-name-layout-ice-94961-2.stderr | 2 +- tests/ui/feature-gates/feature-gate-stride.rs | 6 + .../feature-gates/feature-gate-stride.stderr | 12 ++ tests/ui/layout/layout-cycle.stderr | 1 + tests/ui/layout/swift/struct-offsets.rs | 36 +++++ 39 files changed, 358 insertions(+), 50 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-stride.rs create mode 100644 tests/ui/feature-gates/feature-gate-stride.stderr create mode 100644 tests/ui/layout/swift/struct-offsets.rs diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs index 6b4963ae92461..d8d4558c01209 100644 --- a/compiler/rustc_abi/src/canon_abi.rs +++ b/compiler/rustc_abi/src/canon_abi.rs @@ -35,7 +35,7 @@ pub enum CanonAbi { /// Swift calling convention, exposed via LLVM's `swiftcc`. Cross-platform /// and not tied to a specific target architecture. - Swift, + Swift { tail: bool }, /// ABIs relevant to 32-bit Arm targets Arm(ArmCall), @@ -66,7 +66,7 @@ impl CanonAbi { | CanonAbi::RustTail => true, CanonAbi::C | CanonAbi::Custom - | CanonAbi::Swift + | CanonAbi::Swift { .. } | CanonAbi::Arm(_) | CanonAbi::GpuKernel | CanonAbi::Interrupt(_) @@ -87,7 +87,7 @@ impl fmt::Display for CanonAbi { CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone, CanonAbi::RustTail => ExternAbi::RustTail, CanonAbi::Custom => ExternAbi::Custom, - CanonAbi::Swift => ExternAbi::Swift, + CanonAbi::Swift { .. } => ExternAbi::Swift, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false }, ArmCall::CCmseNonSecureCall => ExternAbi::CmseNonSecureCall, diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e8779d6ee6869..eaa0c8d3b4f57 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -194,6 +194,7 @@ impl LayoutCalculator { largest_niche: element.largest_niche.filter(|_| count != 0), uninhabited: element.uninhabited && count != 0, align: element.align, + size_without_padding: size, size, max_repr_align: None, unadjusted_abi_align: element.align.abi, @@ -512,6 +513,7 @@ impl LayoutCalculator { largest_niche: None, uninhabited: false, align: AbiAlign::new(align), + size_without_padding: size, size: size.align_to(align), max_repr_align, unadjusted_abi_align, @@ -742,6 +744,7 @@ impl LayoutCalculator { backend_repr: abi, largest_niche, uninhabited, + size_without_padding: size, size, align: AbiAlign::new(align), max_repr_align, @@ -1051,6 +1054,7 @@ impl LayoutCalculator { uninhabited, backend_repr: abi, align: AbiAlign::new(align), + size_without_padding: size, size, max_repr_align, unadjusted_abi_align, @@ -1294,8 +1298,9 @@ impl LayoutCalculator { } } + let field_size = if repr.swift() { field.size_without_padding } else { field.size }; offset = - offset.checked_add(field.size, dl).ok_or(LayoutCalculatorError::SizeOverflow)?; + offset.checked_add(field_size, dl).ok_or(LayoutCalculatorError::SizeOverflow)?; } // The unadjusted ABI alignment does not include repr(align), but does include repr(pack). @@ -1417,6 +1422,7 @@ impl LayoutCalculator { largest_niche, uninhabited, align: AbiAlign::new(align), + size_without_padding: min_size, size, max_repr_align, unadjusted_abi_align, @@ -1516,6 +1522,7 @@ where backend_repr: repr, largest_niche: elt.largest_niche, uninhabited: false, + size_without_padding: size, size, align: AbiAlign::new(align), max_repr_align: None, diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs index fd68d06c93829..7207b64f21b2f 100644 --- a/compiler/rustc_abi/src/layout/coroutine.rs +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -306,6 +306,7 @@ pub(super) fn layout< // FIXME: Remove when is implemented and aliased coroutine fields are wrapped in `UnsafePinned`. largest_niche: None, uninhabited, + size_without_padding: size, size, align, max_repr_align: None, diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index 1fffb84ec21cb..9cda6d8168192 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -22,6 +22,7 @@ impl LayoutData { largest_niche: None, uninhabited: false, align: AbiAlign::new(dl.i8_align), + size_without_padding: Size::ZERO, size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align, @@ -39,6 +40,7 @@ impl LayoutData { largest_niche: None, uninhabited: true, align: AbiAlign::new(dl.i8_align), + size_without_padding: Size::ZERO, size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align, @@ -80,6 +82,7 @@ impl LayoutData { backend_repr: BackendRepr::Scalar(scalar), largest_niche, uninhabited: false, + size_without_padding: size, size, align, max_repr_align: None, @@ -114,6 +117,7 @@ impl LayoutData { largest_niche, uninhabited: false, align: AbiAlign::new(align), + size_without_padding: size, size, max_repr_align: None, unadjusted_abi_align: align, @@ -140,6 +144,7 @@ impl LayoutData { largest_niche: None, uninhabited: true, align: AbiAlign::new(dl.i8_align), + size_without_padding: Size::ZERO, size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align, @@ -166,6 +171,7 @@ impl LayoutData { largest_niche: layout.largest_niche, uninhabited: layout.uninhabited, size: layout.size, + size_without_padding: layout.size_without_padding, align: parent.align, max_repr_align: parent.max_repr_align, unadjusted_abi_align: parent.unadjusted_abi_align, diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 1e0fd78b4dd75..240f1971cafe3 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -101,11 +101,13 @@ bitflags! { /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5; const IS_SCALABLE = 1 << 6; + const IS_SWIFT = 1 << 7; // Any of these flags being set prevent field reordering optimisation. const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits() | ReprFlags::IS_SCALABLE.bits() - | ReprFlags::IS_LINEAR.bits(); + | ReprFlags::IS_LINEAR.bits() + | ReprFlags::IS_SWIFT.bits(); const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits(); } } @@ -184,6 +186,11 @@ impl ReprOptions { self.flags.contains(ReprFlags::IS_C) } + #[inline] + pub fn swift(&self) -> bool { + self.flags.contains(ReprFlags::IS_SWIFT) + } + #[inline] pub fn packed(&self) -> bool { self.pack.is_some() @@ -2162,7 +2169,12 @@ pub struct LayoutData { /// especially in the case of by-pointer struct returns, which allocate stack even when unused. pub uninhabited: bool, + /// The alignment of the type in memory. pub align: AbiAlign, + /// The amount of memory occupied by this type, this excludes padding. + pub size_without_padding: Size, + /// The stride of the type is its size including padding. Or how much you need to move in + /// memory to get from one element to the next. pub size: Size, /// The largest alignment explicitly requested with `repr(align)` on this type or any field. @@ -2226,6 +2238,7 @@ where // `Interned`. We print it like this to avoid having to update // expected output in a lot of tests. let LayoutData { + size_without_padding: min_size, size, align, backend_repr, @@ -2238,6 +2251,7 @@ where randomization_seed, } = self; f.debug_struct("Layout") + .field("min_size", min_size) .field("size", size) .field("align", align) .field("backend_repr", backend_repr) @@ -2388,6 +2402,7 @@ pub enum AbiFromStrErr { #[cfg_attr(feature = "nightly", derive(StableHash))] pub struct VariantLayout { pub size: Size, + pub size_without_padding: Size, pub backend_repr: BackendRepr, pub field_offsets: IndexVec, fields_in_memory_order: IndexVec, @@ -2403,6 +2418,7 @@ impl VariantLayout { Self { size: layout.size, + size_without_padding: layout.size_without_padding, backend_repr: layout.backend_repr, field_offsets: offsets, fields_in_memory_order: in_memory_order, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d1368d8f9f633..d5bc1a110695a 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -567,7 +567,7 @@ impl<'a> AstValidator<'a> { | CanonAbi::RustCold | CanonAbi::RustPreserveNone | CanonAbi::RustTail - | CanonAbi::Swift + | CanonAbi::Swift { .. } | CanonAbi::Arm(_) | CanonAbi::X86(_) => { /* nothing to check */ } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index d24064b793390..02bec99ebdca1 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -76,7 +76,7 @@ pub(crate) fn conv_to_call_conv( _ => default_call_conv, }, - CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => { + CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift { .. } => { sess.dcx().fatal(format!("call conv {c:?} is not yet implemented")) } CanonAbi::GpuKernel => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 1b7bb8c907735..b567689299437 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -248,6 +248,8 @@ pub fn conv_to_fn_attribute<'gcc>(sess: &Session, conv: CanonAbi) -> Option FnAttribute::Cold, + // gcc doesn't support Swift as far as I'm aware + CanonAbi::Swift { .. } => return None, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 65bb32ee666f2..a3685d115439e 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -733,7 +733,11 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm: // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. CanonAbi::Custom => llvm::CCallConv, - CanonAbi::Swift => llvm::SwiftCallConv, + CanonAbi::Swift { tail } => if tail { + llvm::SwiftCallConvTail + } else { + llvm::SwiftCallConv + } CanonAbi::GpuKernel => match &sess.target.arch { Arch::AmdGpu => llvm::AmdgpuKernel, Arch::Nvptx64 => llvm::PtxKernel, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1a60b59a93525..f550dbc240ebf 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -167,6 +167,7 @@ pub(crate) enum CallConv { PreserveAll = 15, SwiftCallConv = 16, Tail = 18, + SwiftCallConvTail = 20, PreserveNone = 21, X86StdcallCallConv = 64, X86FastcallCallConv = 65, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index cc48f418d03db..4f89d91e4dfbe 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -164,13 +164,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::size_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); - let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); + let (llsize, _, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); OperandValue::Immediate(llsize) } + sym::stride_of_val => { + let tp_ty = fn_args.type_at(0); + let (_, meta) = args[0].val.pointer_parts(); + let (_, llstride, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); + OperandValue::Immediate(llstride) + } sym::align_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); - let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); + let (_, _, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); OperandValue::Immediate(llalign) } sym::vtable_size | sym::vtable_align => { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 14a5f71fbceaa..16d846d8d6510 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -228,7 +228,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let unaligned_offset = bx.cx().const_usize(offset.bytes()); // Get the alignment of the field - let (_, mut unsized_align) = size_of_val::size_and_align_of_dst(bx, field.ty, meta); + let (_, _, mut unsized_align) = size_of_val::size_and_align_of_dst(bx, field.ty, meta); // For packed types, we need to cap alignment. if let ty::Adt(def, _) = self.layout.ty.kind() diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 52ffc321cbb6f..002228713c58e 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -16,13 +16,14 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, t: Ty<'tcx>, info: Option, -) -> (Bx::Value, Bx::Value) { +) -> (Bx::Value, Bx::Value, Bx::Value) { let layout = bx.layout_of(t); trace!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout); if layout.is_sized() { - let size = bx.const_usize(layout.size.bytes()); + let size = bx.const_usize(layout.size_without_padding.bytes()); + let stride = bx.const_usize(layout.size.bytes()); let align = bx.const_usize(layout.align.bytes()); - return (size, align); + return (size, stride, align); } match t.kind() { ty::Dynamic(..) => { @@ -41,18 +42,19 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let align_bound = Align::max_for_target(bx.data_layout()).bytes().into(); bx.range_metadata(align, WrappingRange { start: 1, end: align_bound }); - (size, align) + (size, size, align) } ty::Slice(_) | ty::Str => { let unit = layout.field(bx, 0); // The info in this case is the length of the str, so the size is that // times the unit size. - ( - // All slice sizes must fit into `isize`, so this multiplication cannot - // wrap -- neither signed nor unsigned. - bx.unchecked_sumul(info.unwrap(), bx.const_usize(unit.size.bytes())), - bx.const_usize(unit.align.bytes()), - ) + let info = info.unwrap(); + // All slice sizes must fit into `isize`, so this multiplication cannot + // wrap -- neither signed nor unsigned. + let size = bx.unchecked_sumul(info, bx.const_usize(unit.size_without_padding.bytes())); + let stride = bx.unchecked_sumul(info, bx.const_usize(unit.size.bytes())); + let align = bx.const_usize(unit.align.bytes()); + (size, stride, align) } ty::Foreign(_) => { // `extern` type. We cannot compute the size, so panic. @@ -83,9 +85,10 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ); // This function does not return so we can now return whatever we want. - let size = bx.const_usize(layout.size.bytes()); + let size = bx.const_usize(layout.size_without_padding.bytes()); + let stride = bx.const_usize(layout.size.bytes()); let align = bx.const_usize(layout.align.bytes()); - (size, align) + (size, stride, align) } ty::Adt(..) | ty::Tuple(..) => { // First get the size of all statically known fields. @@ -107,7 +110,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Recurse to get the size of the dynamically sized field (must be // the last field). let field_ty = layout.field(bx, i).ty; - let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); + let (_, unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); // # First compute the dynamic alignment @@ -175,9 +178,9 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let addend = bx.sub(full_align, one); let add = bx.add(full_size, addend); let neg = bx.neg(full_align); - let full_size = bx.and(add, neg); + let full_stride = bx.and(add, neg); - (full_size, full_align) + (full_size, full_stride, full_align) } _ => bug!("size_and_align_of_dst: {t} not supported"), } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 45d57f77a4079..d19494c1508b6 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -430,7 +430,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout: &TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { if layout.is_sized() { - return interp_ok(Some((layout.size, layout.align.abi))); + return interp_ok(Some((layout.size_without_padding, layout.align.abi))); } match layout.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { @@ -473,7 +473,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # Then compute the dynamic size let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align); - let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align); + let full_size = unsized_offset_adjusted + unsized_size; // Just for our sanitiy's sake, assert that this is equal to what codegen would compute. assert_eq!( @@ -511,6 +511,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => span_bug!(self.cur_span(), "size_and_align_of::<{}> not supported", layout.ty), } } + #[inline] pub fn size_and_align_of_val( &self, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index f8c308ece55b7..adf68cc0ca60e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -219,6 +219,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !layout.is_sized() { span_bug!(self.cur_span(), "unsized type for `size_of`"); } + let val = layout.size_without_padding.bytes(); + self.write_scalar(Scalar::from_target_usize(val, self), dest)?; + } + sym::stride_of => { + let tp_ty = instance.args.type_at(0); + let layout = self.layout_of(tp_ty)?; + if !layout.is_sized() { + span_bug!(self.cur_span(), "unsized type for `stride_of`"); + } let val = layout.size.bytes(); self.write_scalar(Scalar::from_target_usize(val, self), dest)?; } @@ -299,7 +308,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&val, dest)?; } - sym::align_of_val | sym::size_of_val => { + sym::align_of_val | sym::size_of_val | sym::stride_of_val => { // Avoid `deref_pointer` -- this is not a deref, the ptr does not have to be // dereferenceable! let place = self.imm_ptr_to_mplace(&self.read_immediate(&args[0])?)?; @@ -310,6 +319,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let result = match intrinsic_name { sym::align_of_val => align.bytes(), sym::size_of_val => size.bytes(), + sym::stride_of_val => size.align_to(align).bytes(), _ => bug!(), }; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index a8f5e406e29ee..ac1dc5ec743cd 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -43,7 +43,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { 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"); - interp_ok((layout.size, layout.align.abi)) + interp_ok((layout.size_without_padding, layout.align.abi)) } pub(super) fn vtable_entries( diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index b15ea5da6f0cb..32475a9e75e0b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -288,6 +288,8 @@ declare_features! ( (internal, rustc_attrs, "1.0.0", None), /// Allows using the `#[stable]` and `#[unstable]` attributes. (internal, staged_api, "1.0.0", None), + /// Introduces the split between size and stride. + (unstable, stride, "CURRENT_RUSTC_VERSION", None), /// Perma-unstable, only used to test the `incomplete_features` lint. (incomplete, test_incomplete_feature, "1.96.0", None), /// Added for testing unstable lints; perma-unstable. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 94241e6a31eb0..f15830f44968a 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -177,6 +177,7 @@ pub enum ReprAttr { ReprInt(IntType), ReprRust, ReprC, + ReprSwift, ReprPacked(Align), ReprSimd, ReprTransparent, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9d8b0e101d374..d6a55747ad224 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -163,6 +163,7 @@ language_item_table! { Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); AlignOf, sym::mem_align_const, align_const, Target::AssocConst, GenericRequirement::Exact(0); SizeOf, sym::mem_size_const, size_const, Target::AssocConst, GenericRequirement::Exact(0); + StrideOf, sym::mem_stride_const, stride_const, Target::AssocConst, GenericRequirement::Exact(0); OffsetOf, sym::offset_of, offset_of, Target::Fn, GenericRequirement::Exact(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 67fddd87fbb1d..5bdff60c6c8cc 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -209,6 +209,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::sqrtf32 | sym::sqrtf64 | sym::sqrtf128 + | sym::stride_of | sym::sub_with_overflow | sym::three_way_compare | sym::truncf16 @@ -301,8 +302,8 @@ pub(crate) fn check_intrinsic_type( sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), - sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), - sym::size_of_val | sym::align_of_val => { + sym::size_of | sym::stride_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), + sym::size_of_val | sym::stride_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index d2430a06a0072..b7b6cc1f8fa7e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -222,7 +222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | CanonAbi::RustCold | CanonAbi::RustPreserveNone | CanonAbi::RustTail - | CanonAbi::Swift + | CanonAbi::Swift { .. } | CanonAbi::Arm(_) | CanonAbi::X86(_) => {} } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 149fa69f2abbb..2b16b51e28aea 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1725,6 +1725,7 @@ impl<'tcx> TyCtxt<'tcx> { flags.insert(match *r { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, + attr::ReprSwift => ReprFlags::IS_SWIFT, attr::ReprPacked(pack) => { min_pack = Some(if let Some(min_pack) = min_pack { min_pack.min(pack) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a5ba6d2f14d12..5a1b853dcc2f6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1184,6 +1184,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ReprAttr::ReprC => { is_c = true; } + ReprAttr::ReprSwift => match target { + Target::Struct | Target::Union | Target::Enum => continue, + _ => { + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); + } + }, ReprAttr::ReprAlign(..) => {} ReprAttr::ReprPacked(_) => {} ReprAttr::ReprSimd => { diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 02674e4107c77..1414443572aaf 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -496,6 +496,7 @@ pub enum CallConvention { pub struct ReprFlags { pub is_simd: bool, pub is_c: bool, + pub is_swift: bool, pub is_transparent: bool, pub is_linear: bool, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 31104ce897ffb..2d3f54c80e596 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -127,7 +127,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi { CanonAbi::RustPreserveNone => CallConvention::PreserveNone, CanonAbi::RustTail => CallConvention::Tail, CanonAbi::Custom => CallConvention::Custom, - CanonAbi::Swift => CallConvention::Swift, + CanonAbi::Swift { .. } => CallConvention::Swift, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => CallConvention::ArmAapcs, ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall, @@ -414,6 +414,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ReprFlags { ReprFlags { is_simd: self.intersects(Self::IS_SIMD), is_c: self.intersects(Self::IS_C), + is_swift: self.intersects(Self::IS_SWIFT), is_transparent: self.intersects(Self::IS_TRANSPARENT), is_linear: self.intersects(Self::IS_LINEAR), } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8d6661ca1194b..d3bd363dcce69 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -319,6 +319,7 @@ symbols! { String, Struct, StructuralPartialEq, + Swift, SymbolIntern, Sync, SyncUnsafeCell, @@ -1291,6 +1292,7 @@ symbols! { mem_drop, mem_forget, mem_size_const, + mem_stride_const, mem_swap, mem_uninitialized, mem_variant_count, @@ -2063,6 +2065,9 @@ symbols! { str_inherent_from_utf8_unchecked, str_inherent_from_utf8_unchecked_mut, strict_provenance_lints, + stride, + stride_of, + stride_of_val, string_deref_patterns, stringify, struct_field_attributes, diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index deea6465118ed..3d026bf260dfb 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -743,6 +743,39 @@ impl<'a, Ty> FnAbi<'a, Ty> { } } + pub fn adjust_for_swift_abi(&mut self, _cx: &C) + where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec + HasX86AbiOpt, + { + for arg in self.args.iter_mut() { + if arg.is_ignore() { + continue; + } + // Small structs get their innards splattered inline + if arg.layout.layout.size.bytes() <= 24 { + let mut prefix: [Option; 8] = [None; 8]; + let mut size = arg.layout.size; + for i in 0..8 { + let reg_size = match size.bytes() { + 8.. => 8, + 4.. => 4, + 2.. => 2, + 1.. => 1, + 0 => break, + }; + prefix[i] = + Some(Reg { kind: RegKind::Integer, size: Size::from_bytes(reg_size) }); + size = Size::from_bytes(size.bytes() - reg_size); + } + arg.cast_to(CastTarget::prefixed( + prefix, + Uniform::new(Reg::i8(), Size::from_bytes(0)), + )); + } + } + } + pub fn adjust_for_rust_abi(&mut self, cx: &C) where Ty: TyAbiInterface<'a, C> + Copy, diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index dd69afec47975..15d0d6d4283fe 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -104,7 +104,7 @@ impl AbiMap { (ExternAbi::RustPreserveNone, _) => CanonAbi::RustPreserveNone, (ExternAbi::RustTail, _) => CanonAbi::RustTail, - (ExternAbi::Swift, _) => CanonAbi::Swift, + (ExternAbi::Swift, _) => CanonAbi::Swift { tail: false }, (ExternAbi::System { .. }, ArchKind::X86) if os == OsKind::Windows && !has_c_varargs => diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index ad8918f70a582..87c26c52c315b 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -659,6 +659,9 @@ fn fn_abi_adjust_for_abi<'tcx>( fn_abi.adjust_for_rust_abi(cx); } else { fn_abi.adjust_for_foreign_abi(cx, abi); + if abi == ExternAbi::Swift { + fn_abi.adjust_for_swift_abi(cx); + } } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index fad2cb08a8a64..ade48f1b42e89 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2944,7 +2944,7 @@ pub unsafe fn vtable_size(ptr: *const ()) -> usize; #[rustc_intrinsic] pub unsafe fn vtable_align(ptr: *const ()) -> usize; -/// The size of a type in bytes. +/// The size of a type in bytes, excluding alignment padding. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -2952,20 +2952,28 @@ pub unsafe fn vtable_align(ptr: *const ()) -> usize; /// any safety invariants. /// /// More specifically, this is the offset in bytes between successive -/// items of the same type, including alignment padding. +/// items of the same type, excluding alignment padding. /// /// Note that, unlike most intrinsics, this can only be called at compile-time /// as backends do not have an implementation for it. The only caller (its /// stable counterpart) wraps this intrinsic call in a `const` block so that /// backends only see an evaluated constant. /// -/// The stabilized version of this intrinsic is [`core::mem::size_of`]. +/// The stabilized version of this intrinsic is [`core::mem::size_without_padding_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_comptime] -pub fn size_of() -> usize; +pub const fn size_of() -> usize; + +/// See [`size_of`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic_const_stable_indirect] +#[rustc_intrinsic] +#[rustc_comptime] +pub const fn stride_of() -> usize; /// The minimum alignment of a type. /// @@ -3051,6 +3059,19 @@ pub fn variant_count() -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn size_of_val(ptr: *const T) -> usize; +/// The stride of the referenced value in bytes. +/// +/// The stabilized version of this intrinsic is [`core::mem::stride_of_val`]. +/// +/// # Safety +/// +/// See [`crate::mem::size_of_val_raw`] for safety conditions. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_intrinsic_const_stable_indirect] +pub const unsafe fn stride_of_val(ptr: *const T) -> usize; + /// The required alignment of the referenced value. /// /// The stabilized version of this intrinsic is [`core::mem::align_of_val`]. diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9a2ad0004ae4c..9d43f97bd7059 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -380,7 +380,16 @@ pub const fn size_of() -> usize { // size_of on the type to ensure it exists, so if we fully DCE'd size_of calls that would be // considered unsound... but by making this a constant, it participates in the usual "required // consts" system, and we are safe. - ::SIZE + ::STRIDE +} + +/// See [`size_of`]. +#[inline(always)] +#[must_use] +#[unstable(feature = "stride", issue = "none")] +#[rustc_diagnostic_item = "mem_stride_of"] +pub const fn stride_of() -> usize { + ::STRIDE } /// Returns the size of the pointed-to value in bytes. @@ -409,7 +418,52 @@ pub const fn size_of() -> usize { #[rustc_diagnostic_item = "mem_size_of_val"] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer - unsafe { intrinsics::size_of_val(val) } + unsafe { intrinsics::stride_of_val(val) } +} + +/// Returns the size of a type without its padding in bytes. +/// +/// More specifically, this is the size in bytes of an item of the provided type, +/// excluding alignment padding. +/// +/// In general, the size of a type is not stable across compilations, but +/// specific types such as primitives are. See the table in [`size_of`]. +/// +/// ## Size of Structs +/// +/// For `struct`s, the size is determined by the following algorithm. +/// +/// For each field in the struct ordered by declaration order: +/// +/// 1. Add the size of the field. +/// 2. Round up the current size to the nearest multiple of the next field's [alignment]. +/// +/// Unlike `C`, zero sized structs are not rounded up to one byte in size. +/// +/// ## Size of Enums +/// +/// Enums that carry no data other than the discriminant have the same size as C enums +/// on the platform they are compiled for. +/// +/// ## Size of Unions +/// +/// The size of a union is the size of its largest field. +/// +/// Unlike `C`, zero sized unions are not rounded up to one byte in size. +/// +/// ## Size of Fields & Arrays +/// +/// Must add examples as the size of a tuple is the sum of strides except for the last item which uses its size. +/// Arrays always use the stride. +/// +/// [alignment]: align_of +/// +#[inline(always)] +#[must_use] +#[unstable(feature = "stride", issue = "none")] +#[rustc_diagnostic_item = "mem_size_without_padding_of"] +pub const fn size_without_padding_of() -> usize { + ::SIZE } /// Returns the size of the pointed-to value in bytes. @@ -464,10 +518,68 @@ pub const fn size_of_val(val: &T) -> usize { #[stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn size_of_val_raw(val: *const T) -> usize { + // SAFETY: the caller must provide a valid raw pointer + unsafe { intrinsics::stride_of_val(val) } +} + +/// Returns the size of the pointed-to value in bytes. +/// +/// This is usually the same as [`size_of::()`]. However, when `T` *has* no +/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], +/// then `size_of_val` can be used to get the dynamically-known size. +/// +/// [trait object]: ../../book/ch17-02-trait-objects.html +/// +/// # Examples +/// +/// ``` +/// assert_eq!(4, size_of_val(&5i32)); +/// +/// let x: [u8; 13] = [0; 13]; +/// let y: &[u8] = &x; +/// assert_eq!(13, size_of_val(y)); +/// ``` +/// +/// [`size_of::()`]: size_of +#[inline] +#[must_use] +#[unstable(feature = "stride", issue = "none")] +#[rustc_diagnostic_item = "mem_size_without_padding_of_val"] +pub const fn size_without_padding_of_val(val: &T) -> usize { + // SAFETY: `val` is a reference, so it's a valid raw pointer + unsafe { intrinsics::size_of_val(val) } +} + +/// See [size_without_padding_of_val]. +#[inline] +#[must_use] +#[unstable(feature = "stride", issue = "none")] +#[rustc_diagnostic_item = "mem_size_without_padding_of_val_raw"] +pub const unsafe fn size_without_padding_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::size_of_val(val) } } +/// See [`size_of_val`]. +#[inline] +#[must_use] +#[unstable(feature = "stride", issue = "none")] +#[rustc_diagnostic_item = "mem_stride_of_val"] +pub const fn stride_of_val(val: &T) -> usize { + // SAFETY: `val` is a reference, so it's a valid raw pointer + unsafe { intrinsics::stride_of_val(val) } +} + +/// See [`size_of_val`]. +#[inline] +#[must_use] +#[unstable(feature = "stride", issue = "none")] +#[rustc_diagnostic_item = "mem_stride_without_padding_of_val_raw"] +pub const unsafe fn stride_of_val_raw(val: *const T) -> usize { + // SAFETY: the caller must provide a valid raw pointer + unsafe { intrinsics::stride_of_val(val) } +} + /// Returns the [ABI]-required minimum alignment of a type in bytes. /// /// Every reference to a value of the type `T` must be a multiple of this number. @@ -1219,7 +1331,7 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { b: ManuallyDrop, } - match const { Ord::cmp(&Src::SIZE, &Dst::SIZE) } { + match const { Ord::cmp(&Src::STRIDE, &Dst::STRIDE) } { // SAFETY: When Dst is bigger, the union is the size of Dst Ordering::Less => unsafe { let a = transmute_neo(src); @@ -1263,7 +1375,7 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { #[inline] #[rustc_no_writable] pub const unsafe fn transmute_neo(src: Src) -> Dst { - const { assert!(Src::SIZE == Dst::SIZE) }; + const { assert!(Src::STRIDE == Dst::STRIDE) }; // SAFETY: the const-assert just checked that they're the same size, // and any other safety invariants need to be upheld by the caller. @@ -1463,6 +1575,11 @@ pub trait SizedTypeProperties: Sized { #[lang = "mem_size_const"] const SIZE: usize = intrinsics::size_of::(); + #[doc(hidden)] + #[unstable(feature = "sized_type_properties", issue = "none")] + #[lang = "mem_stride_const"] + const STRIDE: usize = intrinsics::stride_of::(); + #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] #[lang = "mem_align_const"] @@ -1502,7 +1619,7 @@ pub trait SizedTypeProperties: Sized { /// ``` #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] - const IS_ZST: bool = Self::SIZE == 0; + const IS_ZST: bool = Self::STRIDE == 0; #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] @@ -1510,7 +1627,7 @@ pub trait SizedTypeProperties: Sized { // SAFETY: if the type is instantiated, rustc already ensures that its // layout is valid. Use the unchecked constructor to avoid inserting a // panicking codepath that needs to be optimized out. - unsafe { Layout::from_size_align_unchecked(Self::SIZE, Self::ALIGN) } + unsafe { Layout::from_size_align_unchecked(Self::STRIDE, Self::ALIGN) } }; /// The largest safe length for a `[Self]`. @@ -1519,7 +1636,7 @@ pub trait SizedTypeProperties: Sized { /// which is never allowed for a single object. #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] - const MAX_SLICE_LEN: usize = match Self::SIZE { + const MAX_SLICE_LEN: usize = match Self::STRIDE { 0 => usize::MAX, n => (isize::MAX as usize) / n, }; diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 41f4b837efaea..a8d194d1f8b84 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -557,7 +557,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), - size: usize = size_of::(), + size: usize = size_without_padding_of::(), align: usize = align_of::(), count: usize = count, ) => { diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 3a62e7f61b2b4..a131c73460514 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -153,7 +153,7 @@ where // for reading `len` values, which also means the size is guaranteed // not to overflow because it exists in memory; unsafe { - let size = crate::intrinsics::unchecked_mul(len, Self::SIZE); + let size = crate::intrinsics::unchecked_mul(len, Self::STRIDE); compare_bytes(lhs as _, rhs as _, size) == 0 } } diff --git a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.stderr b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.stderr index 73e1578242675..0b217b5869aa0 100644 --- a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.stderr +++ b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.stderr @@ -1,7 +1,7 @@ error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | - = note: evaluation of ` as std::mem::SizedTypeProperties>::SIZE` failed here + = note: evaluation of ` as std::mem::SizedTypeProperties>::STRIDE` failed here note: the above error was encountered while instantiating `fn std::mem::size_of::>` --> $DIR/debuginfo-type-name-layout-ice-94961-1.rs:13:5 diff --git a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.stderr b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.stderr index 4ce1e2e407e9d..9785948d0432c 100644 --- a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.stderr +++ b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.stderr @@ -1,7 +1,7 @@ error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | - = note: evaluation of ` as std::mem::SizedTypeProperties>::SIZE` failed here + = note: evaluation of ` as std::mem::SizedTypeProperties>::STRIDE` failed here note: the above error was encountered while instantiating `fn std::mem::size_of::>` --> $DIR/debuginfo-type-name-layout-ice-94961-2.rs:16:5 diff --git a/tests/ui/feature-gates/feature-gate-stride.rs b/tests/ui/feature-gates/feature-gate-stride.rs new file mode 100644 index 0000000000000..21ed123b6da01 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-stride.rs @@ -0,0 +1,6 @@ +use std::mem; + +fn main() { + let tmp = mem::stride_of::(); + //~^ ERROR use of unstable library feature `stride` +} diff --git a/tests/ui/feature-gates/feature-gate-stride.stderr b/tests/ui/feature-gates/feature-gate-stride.stderr new file mode 100644 index 0000000000000..321293538699f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-stride.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature `stride` + --> $DIR/feature-gate-stride.rs:4:15 + | +LL | let tmp = mem::stride_of::(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(stride)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/layout/layout-cycle.stderr b/tests/ui/layout/layout-cycle.stderr index 346bae58c1b2e..52f601e358f4f 100644 --- a/tests/ui/layout/layout-cycle.stderr +++ b/tests/ui/layout/layout-cycle.stderr @@ -11,6 +11,7 @@ LL | type I: Tr; | ^^^^^^^^^^ = note: ...which again requires computing layout of `S>`, completing the cycle note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE` +note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::STRIDE` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL = note: for more information, see and diff --git a/tests/ui/layout/swift/struct-offsets.rs b/tests/ui/layout/swift/struct-offsets.rs new file mode 100644 index 0000000000000..c46251ce5f0cf --- /dev/null +++ b/tests/ui/layout/swift/struct-offsets.rs @@ -0,0 +1,36 @@ +//@ run-pass +//@ reference: layout.swift.struct-offsets +//@ edition: 2024 + +// LLVM <{ i64, i8 }> +#[repr(Swift)] +struct S { + a: isize, + b: u8, +} + +// LLVM <{ i8, [7 x i8], <{ i64, i8 }>, i8, [6 x i8] i64, i64 }> +#[repr(Swift)] +struct S2 { + a: u8, + b: S, + c: u8, + d: isize, + e: (), + f: isize, +} + +fn main() { + assert_eq!(core::mem::offset_of!(S, a), 0); + assert_eq!(core::mem::offset_of!(S, b), 8); + + assert_eq!(core::mem::offset_of!(S2, a), 0); + assert_eq!(core::mem::offset_of!(S2, b), 8); + assert_eq!(core::mem::offset_of!(S2, c), 17); + assert_eq!(core::mem::offset_of!(S2, d), 24); + assert_eq!(core::mem::offset_of!(S2, e), 32); + assert_eq!(core::mem::offset_of!(S2, f), 32); + + assert_eq!(core::mem::size_of::(), 40); + assert_eq!(core::mem::align_of::(), 8); +} From 85dd173371a01be1d9afccba3bcf7fe781e232aa Mon Sep 17 00:00:00 2001 From: Christophe Bronner Date: Sat, 8 Aug 2026 12:55:18 -0400 Subject: [PATCH 2/2] Fix build --- .../rustc_attr_parsing/src/attributes/repr.rs | 13 +++ compiler/rustc_passes/src/check_attr.rs | 10 +-- compiler/rustc_target/src/callconv/mod.rs | 7 +- library/core/src/intrinsics/mod.rs | 4 +- library/core/src/lib.rs | 1 + library/core/src/mem/mod.rs | 87 +------------------ library/core/src/ptr/mod.rs | 2 +- 7 files changed, 23 insertions(+), 101 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index bb0da73df015c..e5e36ac986cbd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -141,6 +141,19 @@ fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option< cx.expect_no_args(param.args())?; Some(ReprC) } + Some(sym::Swift) => { + cx.check_target( + "(Swift)", + &AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Warn(Target::MacroCall), + ]), + ); + cx.expect_no_args(param.args())?; + Some(ReprSwift) + } Some(sym::simd) => { if cx.features.is_some_and(|feats| !feats.repr_simd()) { feature_err( diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5a1b853dcc2f6..549d0463e6495 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1184,15 +1184,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ReprAttr::ReprC => { is_c = true; } - ReprAttr::ReprSwift => match target { - Target::Struct | Target::Union | Target::Enum => continue, - _ => { - self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: *repr_span, - span, - }); - } - }, + ReprAttr::ReprSwift => {}, ReprAttr::ReprAlign(..) => {} ReprAttr::ReprPacked(_) => {} ReprAttr::ReprSimd => { diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 3d026bf260dfb..1aca365d0eebd 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -754,9 +754,9 @@ impl<'a, Ty> FnAbi<'a, Ty> { } // Small structs get their innards splattered inline if arg.layout.layout.size.bytes() <= 24 { - let mut prefix: [Option; 8] = [None; 8]; + let mut prefix: ArrayVec = ArrayVec::new(); let mut size = arg.layout.size; - for i in 0..8 { + for _i in 0..8 { let reg_size = match size.bytes() { 8.. => 8, 4.. => 4, @@ -764,8 +764,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { 1.. => 1, 0 => break, }; - prefix[i] = - Some(Reg { kind: RegKind::Integer, size: Size::from_bytes(reg_size) }); + prefix.push(Reg { kind: RegKind::Integer, size: Size::from_bytes(reg_size) }); size = Size::from_bytes(size.bytes() - reg_size); } arg.cast_to(CastTarget::prefixed( diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index ade48f1b42e89..88b0d815bfa89 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2965,7 +2965,7 @@ pub unsafe fn vtable_align(ptr: *const ()) -> usize; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_comptime] -pub const fn size_of() -> usize; +pub fn size_of() -> usize; /// See [`size_of`]. #[rustc_nounwind] @@ -2973,7 +2973,7 @@ pub const fn size_of() -> usize; #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_comptime] -pub const fn stride_of() -> usize; +pub fn stride_of() -> usize; /// The minimum alignment of a type. /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0910e4bc718ea..bd9cb1a606c15 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -184,6 +184,7 @@ #![feature(s390x_target_feature)] #![feature(wasm_target_feature)] #![feature(x86_amx_intrinsics)] +#![feature(stride)] // tidy-alphabetical-end // allow using `core::` in intra-doc links diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9d43f97bd7059..573f239208ad1 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -380,7 +380,7 @@ pub const fn size_of() -> usize { // size_of on the type to ensure it exists, so if we fully DCE'd size_of calls that would be // considered unsound... but by making this a constant, it participates in the usual "required // consts" system, and we are safe. - ::STRIDE + ::SIZE } /// See [`size_of`]. @@ -418,52 +418,7 @@ pub const fn stride_of() -> usize { #[rustc_diagnostic_item = "mem_size_of_val"] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer - unsafe { intrinsics::stride_of_val(val) } -} - -/// Returns the size of a type without its padding in bytes. -/// -/// More specifically, this is the size in bytes of an item of the provided type, -/// excluding alignment padding. -/// -/// In general, the size of a type is not stable across compilations, but -/// specific types such as primitives are. See the table in [`size_of`]. -/// -/// ## Size of Structs -/// -/// For `struct`s, the size is determined by the following algorithm. -/// -/// For each field in the struct ordered by declaration order: -/// -/// 1. Add the size of the field. -/// 2. Round up the current size to the nearest multiple of the next field's [alignment]. -/// -/// Unlike `C`, zero sized structs are not rounded up to one byte in size. -/// -/// ## Size of Enums -/// -/// Enums that carry no data other than the discriminant have the same size as C enums -/// on the platform they are compiled for. -/// -/// ## Size of Unions -/// -/// The size of a union is the size of its largest field. -/// -/// Unlike `C`, zero sized unions are not rounded up to one byte in size. -/// -/// ## Size of Fields & Arrays -/// -/// Must add examples as the size of a tuple is the sum of strides except for the last item which uses its size. -/// Arrays always use the stride. -/// -/// [alignment]: align_of -/// -#[inline(always)] -#[must_use] -#[unstable(feature = "stride", issue = "none")] -#[rustc_diagnostic_item = "mem_size_without_padding_of"] -pub const fn size_without_padding_of() -> usize { - ::SIZE + unsafe { intrinsics::size_of_val(val) } } /// Returns the size of the pointed-to value in bytes. @@ -518,44 +473,6 @@ pub const fn size_without_padding_of() -> usize { #[stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn size_of_val_raw(val: *const T) -> usize { - // SAFETY: the caller must provide a valid raw pointer - unsafe { intrinsics::stride_of_val(val) } -} - -/// Returns the size of the pointed-to value in bytes. -/// -/// This is usually the same as [`size_of::()`]. However, when `T` *has* no -/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], -/// then `size_of_val` can be used to get the dynamically-known size. -/// -/// [trait object]: ../../book/ch17-02-trait-objects.html -/// -/// # Examples -/// -/// ``` -/// assert_eq!(4, size_of_val(&5i32)); -/// -/// let x: [u8; 13] = [0; 13]; -/// let y: &[u8] = &x; -/// assert_eq!(13, size_of_val(y)); -/// ``` -/// -/// [`size_of::()`]: size_of -#[inline] -#[must_use] -#[unstable(feature = "stride", issue = "none")] -#[rustc_diagnostic_item = "mem_size_without_padding_of_val"] -pub const fn size_without_padding_of_val(val: &T) -> usize { - // SAFETY: `val` is a reference, so it's a valid raw pointer - unsafe { intrinsics::size_of_val(val) } -} - -/// See [size_without_padding_of_val]. -#[inline] -#[must_use] -#[unstable(feature = "stride", issue = "none")] -#[rustc_diagnostic_item = "mem_size_without_padding_of_val_raw"] -pub const unsafe fn size_without_padding_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::size_of_val(val) } } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index a8d194d1f8b84..41f4b837efaea 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -557,7 +557,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), - size: usize = size_without_padding_of::(), + size: usize = size_of::(), align: usize = align_of::(), count: usize = count, ) => {