Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions compiler/rustc_abi/src/canon_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -66,7 +66,7 @@ impl CanonAbi {
| CanonAbi::RustTail => true,
CanonAbi::C
| CanonAbi::Custom
| CanonAbi::Swift
| CanonAbi::Swift { .. }
| CanonAbi::Arm(_)
| CanonAbi::GpuKernel
| CanonAbi::Interrupt(_)
Expand All @@ -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,
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
largest_niche: element.largest_niche.filter(|_| count != 0),
uninhabited: element.uninhabited && count != 0,
align: element.align,
size_without_padding: size,

@RalfJung RalfJung Aug 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something as fundamental as adding a new kind of size will at the very least need an MCP if it's something that only ever matters internally to compiler devs, or an RFC if it is relevant to people writing (unsafe) Rust code.

View changes since the review

@JonathanBrouwer JonathanBrouwer Aug 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, and furthermore the attribute change requires a T-lang RFC, see https://github.com/rust-lang/rfcs#what-the-process-is

If you'd only like to like to gauge interest without writing a full RFC immediately, I'd recommend opening a zulip thread explaining your idea

@rustbot author

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cf. #156481, but that was only a lang experiment about swift function call ABI, not repr(swift).

@jieyouxu jieyouxu Aug 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In particular, any repr(Swift) ABI attempts really ought to address #155815 (comment) and #155815 (comment):

When will arguments be marked as swiftself and swifterror in LLVM IR? Without these it is impossible to call class methods and fallible functions respectively afaik. Also how useful is this without repr(Swift)? Delaying implementation of that to another PR would be fine I think, but I would personally like to see at least a plan for how to soundly implement repr(Swift) given that it breaks the stride=size assumption that is true for all current memory layouts.

[...]

To be clear, I'm not going to block this PR on figuring out repr(Swift). I'm just a bit worried that we don't figure out repr(Swift) making extern "Swift" not that useful.

By the way I noticed that a question of mine in the previous attempt hadn't been answered yet: #64582 (comment) https://github.com/swiftlang/swift/blob/main/docs/ABI/CallingConventionSummary.rst seems to suggest like the answer to my question is no it doesn't need a separate classify_arg on Apple platforms, but I'm not sure.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, and furthermore the attribute change requires a T-lang RFC, see https://github.com/rust-lang/rfcs#what-the-process-is

If you'd only like to like to gauge interest without writing a full RFC immediately, I'd recommend opening a zulip thread explaining your idea

@rustbot author

I had one here:
https://rust-lang.zulipchat.com/#narrow/channel/427678-t-lang.2Finterop/topic/Interop.20with.20Swift.2C.20how.20to.20implement.20type.20layout.3F/with/615343099

@RalfJung RalfJung Aug 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and you were given a lot of questions there that need a coherent answer and buy-in from the relevant team(s) before we can begin landing code changes here. You are proposing a very deep-cutting change here, we're not doing such changes without some planning.
I can't tell immediately whether your goal is to expose a new kind of "size" as something Rust programmers can see and need to deal with (needs RFC) or just a new internal representation of types in the compiler (needs MCP).

size,
max_repr_align: None,
unadjusted_abi_align: element.align.abi,
Expand Down Expand Up @@ -512,6 +513,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
largest_niche: None,
uninhabited: false,
align: AbiAlign::new(align),
size_without_padding: size,
size: size.align_to(align),
max_repr_align,
unadjusted_abi_align,
Expand Down Expand Up @@ -742,6 +744,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
backend_repr: abi,
largest_niche,
uninhabited,
size_without_padding: size,
size,
align: AbiAlign::new(align),
max_repr_align,
Expand Down Expand Up @@ -1051,6 +1054,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
uninhabited,
backend_repr: abi,
align: AbiAlign::new(align),
size_without_padding: size,
size,
max_repr_align,
unadjusted_abi_align,
Expand Down Expand Up @@ -1294,8 +1298,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
}
}

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).
Expand Down Expand Up @@ -1417,6 +1422,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
largest_niche,
uninhabited,
align: AbiAlign::new(align),
size_without_padding: min_size,
size,
max_repr_align,
unadjusted_abi_align,
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_abi/src/layout/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ pub(super) fn layout<
// FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
largest_niche: None,
uninhabited,
size_without_padding: size,
size,
align,
max_repr_align: None,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_abi/src/layout/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
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,
Expand All @@ -39,6 +40,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
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,
Expand Down Expand Up @@ -80,6 +82,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
backend_repr: BackendRepr::Scalar(scalar),
largest_niche,
uninhabited: false,
size_without_padding: size,
size,
align,
max_repr_align: None,
Expand Down Expand Up @@ -114,6 +117,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
largest_niche,
uninhabited: false,
align: AbiAlign::new(align),
size_without_padding: size,
size,
max_repr_align: None,
unadjusted_abi_align: align,
Expand All @@ -140,6 +144,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
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,
Expand All @@ -166,6 +171,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
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,
Expand Down
18 changes: 17 additions & 1 deletion compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -2162,7 +2169,12 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
/// 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.
Expand Down Expand Up @@ -2226,6 +2238,7 @@ where
// `Interned<LayoutData>`. 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,
Expand All @@ -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)
Expand Down Expand Up @@ -2388,6 +2402,7 @@ pub enum AbiFromStrErr {
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct VariantLayout<FieldIdx: Idx> {
pub size: Size,
pub size_without_padding: Size,
pub backend_repr: BackendRepr,
pub field_offsets: IndexVec<FieldIdx, Size>,
fields_in_memory_order: IndexVec<u32, FieldIdx>,
Expand All @@ -2403,6 +2418,7 @@ impl<FieldIdx: Idx> VariantLayout<FieldIdx> {

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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 */ }

Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_gcc/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ pub fn conv_to_fn_attribute<'gcc>(sess: &Session, conv: CanonAbi) -> Option<FnAt
sess.dcx().fatal("gcc/gccjit backend does not support RustTail calling convention")
}
CanonAbi::RustCold => 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.
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ pub(crate) enum CallConv {
PreserveAll = 15,
SwiftCallConv = 16,
Tail = 18,
SwiftCallConvTail = 20,
PreserveNone = 21,
X86StdcallCallConv = 64,
X86FastcallCallConv = 65,
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
33 changes: 18 additions & 15 deletions compiler/rustc_codegen_ssa/src/size_of_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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(..) => {
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -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"),
}
Expand Down
Loading
Loading