From a281345beeaf50b234149ac4b7847d8e259083ee Mon Sep 17 00:00:00 2001 From: beetrees Date: Fri, 4 Jul 2025 17:43:45 +0100 Subject: [PATCH 1/2] Add `#[rustc_pass_indirectly_in_non_rustic_abis]` --- compiler/rustc_abi/src/layout/ty.rs | 26 ++++++ compiler/rustc_abi/src/lib.rs | 15 ++-- .../src/attributes.rs | 3 + .../src/encode_cross_crate.rs | 1 + .../src/attributes/codegen_attrs.rs | 7 ++ compiler/rustc_attr_parsing/src/context.rs | 5 +- compiler/rustc_feature/src/builtin_attrs.rs | 6 ++ compiler/rustc_middle/src/ty/layout.rs | 7 +- compiler/rustc_middle/src/ty/mod.rs | 8 ++ compiler/rustc_passes/messages.ftl | 4 + compiler/rustc_passes/src/check_attr.rs | 26 ++++++ compiler/rustc_passes/src/errors.rs | 9 ++ compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/callconv/aarch64.rs | 4 + compiler/rustc_target/src/callconv/mod.rs | 10 +++ compiler/rustc_target/src/callconv/powerpc.rs | 14 +++- compiler/rustc_target/src/callconv/s390x.rs | 4 + compiler/rustc_target/src/callconv/x86_64.rs | 5 ++ .../rustc_target/src/callconv/x86_win64.rs | 11 ++- compiler/rustc_target/src/callconv/xtensa.rs | 26 ++++-- .../abi/pass-indirectly-attr.aarch64.stderr | 84 +++++++++++++++++++ .../abi/pass-indirectly-attr.powerpc.stderr | 84 +++++++++++++++++++ tests/ui/abi/pass-indirectly-attr.rs | 31 +++++++ .../ui/abi/pass-indirectly-attr.s390x.stderr | 84 +++++++++++++++++++ .../ui/abi/pass-indirectly-attr.x86_64.stderr | 84 +++++++++++++++++++ .../attributes/pass-indirectly.aarch64.stderr | 11 +++ tests/ui/attributes/pass-indirectly.rs | 24 ++++++ .../attributes/pass-indirectly.x86_64.stderr | 11 +++ 28 files changed, 584 insertions(+), 21 deletions(-) create mode 100644 tests/ui/abi/pass-indirectly-attr.aarch64.stderr create mode 100644 tests/ui/abi/pass-indirectly-attr.powerpc.stderr create mode 100644 tests/ui/abi/pass-indirectly-attr.rs create mode 100644 tests/ui/abi/pass-indirectly-attr.s390x.stderr create mode 100644 tests/ui/abi/pass-indirectly-attr.x86_64.stderr create mode 100644 tests/ui/attributes/pass-indirectly.aarch64.stderr create mode 100644 tests/ui/attributes/pass-indirectly.rs create mode 100644 tests/ui/attributes/pass-indirectly.x86_64.stderr diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 8d3c10fd770a6..9c9409f2f40aa 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -172,6 +172,8 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug { fn is_tuple(this: TyAndLayout<'a, Self>) -> bool; fn is_unit(this: TyAndLayout<'a, Self>) -> bool; fn is_transparent(this: TyAndLayout<'a, Self>) -> bool; + /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. + fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool; } impl<'a, Ty> TyAndLayout<'a, Ty> { @@ -269,6 +271,30 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_transparent(self) } + /// If this method returns `true`, then this type should always have a `PassMode` of + /// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a + /// non-Rustic ABI (this is true for structs annotated with the + /// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute). Currently this is only used by + /// `VaList`, so this only needs to be handled on architectures where `VaList` requires it: + /// specifically there is a `support_architectures` array in the + /// `check_pass_indirectly_in_non_rustic_abis` function in + /// `compiler/rustc_passes/src/check_attr.rs` which lists all the architectures this needs to be + /// handled on. See the comment near the top of `library/core/src/ffi/va_list.rs` for more + /// details. + /// + /// This function handles transparent types automatically. + pub fn pass_indirectly_in_non_rustic_abis(mut self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + while self.is_transparent() + && let Some((_, field)) = self.non_1zst_field(cx) + { + self = field; + } + Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self) + } + /// Finds the one field that is not a 1-ZST. /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields. pub fn non_1zst_field(&self, cx: &C) -> Option<(FieldIdx, Self)> diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index a438545c76f1c..870c15e32188f 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -86,14 +86,17 @@ bitflags! { const IS_C = 1 << 0; const IS_SIMD = 1 << 1; const IS_TRANSPARENT = 1 << 2; - // Internal only for now. If true, don't reorder fields. - // On its own it does not prevent ABI optimizations. + /// Internal only for now. If true, don't reorder fields. + /// On its own it does not prevent ABI optimizations. const IS_LINEAR = 1 << 3; - // If true, the type's crate has opted into layout randomization. - // Other flags can still inhibit reordering and thus randomization. - // The seed stored in `ReprOptions.field_shuffle_seed`. + /// If true, the type's crate has opted into layout randomization. + /// Other flags can still inhibit reordering and thus randomization. + /// The seed stored in `ReprOptions.field_shuffle_seed`. const RANDOMIZE_LAYOUT = 1 << 4; - // Any of these flags being set prevent field reordering optimisation. + /// If true, the type is always passed indirectly by non-Rustic ABIs. + /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. + const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5; + /// Any of these flags being set prevent field reordering optimisation. const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits() | ReprFlags::IS_LINEAR.bits(); diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 6af15da7d08cb..0607adea42904 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -315,6 +315,9 @@ pub enum AttributeKind { /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, + /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]` + RustcPassIndirectlyInNonRusticAbis(Span), + /// Represents `#[rustc_skip_during_method_dispatch]`. SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span }, diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 8ebd38a6ba7e3..53d05f35298be 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -45,6 +45,7 @@ impl AttributeKind { RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, + RustcPassIndirectlyInNonRusticAbis(..) => No, SkipDuringMethodDispatch { .. } => No, Stability { .. } => Yes, TargetFeature(..) => No, diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 13f560dff38a4..860fc62b5c44a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -330,3 +330,10 @@ impl CombineAttributeParser for TargetFeatureParser { features } } + +pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser; +impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser { + const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 2a01ee58493f6..3b7026a05ccfd 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -16,8 +16,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; use crate::attributes::codegen_attrs::{ - ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser, - TrackCallerParser, UsedParser, + ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, + RustcPassIndirectlyInNonRusticAbisParser, TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; @@ -150,6 +150,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, // tidy-alphabetical-end ]; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7d9915d7f68b7..792516ce5a32b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -497,6 +497,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No), ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes), ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + // See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details. + rustc_attr!( + rustc_pass_indirectly_in_non_rustic_abis, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::No, + "types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic abis." + ), // Limits: ungated!( diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 09379d9d8059f..da70ca965f675 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -3,7 +3,7 @@ use std::{cmp, fmt}; use rustc_abi::{ AddressSpace, Align, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, PointeeInfo, - PointerKind, Primitive, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, + PointerKind, Primitive, ReprFlags, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, TyAbiInterface, VariantIdx, Variants, }; use rustc_error_messages::DiagMessage; @@ -1138,6 +1138,11 @@ where fn is_transparent(this: TyAndLayout<'tcx>) -> bool { matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent()) } + + /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. + fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'tcx>) -> bool { + matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().flags.contains(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS)) + } } /// Calculates whether a function's ABI can unwind or not. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f1b16ea54e634..d163cad830e4a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1589,6 +1589,14 @@ impl<'tcx> TyCtxt<'tcx> { flags.insert(ReprFlags::IS_LINEAR); } + // See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details. + if attr::find_attr!( + self.get_all_attrs(did), + AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) + ) { + flags.insert(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS); + } + ReprOptions { int: size, align: max_align, pack: min_pack, flags, field_shuffle_seed } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 5a94d01e08866..607487aef963f 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -579,6 +579,10 @@ passes_pass_by_value = `pass_by_value` attribute should be applied to a struct, enum or type alias .label = is not a struct, enum or type alias +passes_pass_indirectly_not_a_struct = + `#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to `struct`s + .label = is not a `struct` + passes_proc_macro_bad_sig = {$kind} has incorrect signature passes_remove_fields = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 18b3ab12e2d36..9ae90796eca57 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -230,6 +230,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::PassByValue(attr_span)) => { self.check_pass_by_value(attr_span, span, target) } + Attribute::Parsed(AttributeKind::RustcPassIndirectlyInNonRusticAbis(attr_span)) => { + self.check_pass_indirectly_in_non_rustic_abis(*attr_span, span, target); + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -2106,6 +2109,29 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_pass_indirectly_in_non_rustic_abis( + &self, + attr_span: Span, + span: Span, + target: Target, + ) { + if target != Target::Struct { + self.dcx().emit_err(errors::PassIndirectlyNotAStruct { attr_span, span }); + } + // Currently this attribute is only supported on architectures where it is required for the + // `VaList` type. See the comment near the top of `library/core/src/ffi/va_list.rs` for + // details. + let supported_architectures = ["aarch64", "powerpc", "s390x", "x86_64", "xtensa"]; + let arch = &*self.tcx.sess.target.arch; + if !supported_architectures.contains(&arch) { + span_bug!( + attr_span, + "support for `#[rustc_pass_indirectly_in_non_rustic_abis]` on `{arch}` has not yet been implemented; \ + see the comment near the top of `library/core/src/ffi/va_list.rs` for details" + ); + } + } + fn check_align_value(&self, align: Align, span: Span) { if align.bytes() > 2_u64.pow(29) { // for values greater than 2^29, a different error will be emitted, make sure that happens diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 3ede3c889c8a1..b13bbf8627fc1 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -425,6 +425,15 @@ pub(crate) struct PassByValue { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_pass_indirectly_not_a_struct)] +pub(crate) struct PassIndirectlyNotAStruct { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_allow_incoherent_impl)] pub(crate) struct AllowIncoherentImpl { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 09f01d8704e2a..a74140119dda6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1882,6 +1882,7 @@ symbols! { rustc_partition_codegened, rustc_partition_reused, rustc_pass_by_value, + rustc_pass_indirectly_in_non_rustic_abis, rustc_peek, rustc_peek_liveness, rustc_peek_maybe_init, diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 5c23e7036b043..b86d0baeb942e 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -114,6 +114,10 @@ where // Not touching this... return; } + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + arg.make_indirect(); + return; + } if !arg.layout.is_aggregate() { if kind == AbiKind::DarwinPCS { // On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits, diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 71cc2a455638b..7d93c14b3a458 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -703,6 +703,16 @@ impl<'a, Ty> FnAbi<'a, Ty> { "bpf" => bpf::compute_abi_info(self), arch => panic!("no lowering implemented for {arch}"), } + if cfg!(debug_assertions) { + // When debug assertions are enabled, double check that any argument types annotated + // with the `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute are passed + // indirectly. + for arg in &self.args { + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + assert!(matches!(arg.mode, PassMode::Indirect { on_stack: false, .. })); + } + } + } } pub fn adjust_for_rust_abi(&mut self, cx: &C) diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index 2f6129626b812..67066672eca3f 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,3 +1,5 @@ +use rustc_abi::TyAbiInterface; + use crate::callconv::{ArgAbi, FnAbi}; use crate::spec::HasTargetSpec; @@ -9,7 +11,10 @@ fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { } } -fn classify_arg(cx: &impl HasTargetSpec, arg: &mut ArgAbi<'_, Ty>) { +fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ if arg.is_ignore() { // powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. if cx.target_spec().os == "linux" @@ -20,14 +25,17 @@ fn classify_arg(cx: &impl HasTargetSpec, arg: &mut ArgAbi<'_, Ty>) { } return; } - if arg.layout.is_aggregate() { + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() { arg.make_indirect(); } else { arg.extend_integer_width_to(32); } } -pub(crate) fn compute_abi_info(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { +pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ if !fn_abi.ret.is_ignore() { classify_ret(&mut fn_abi.ret); } diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index d2ae404b23b8b..c2f2b47690cab 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -37,6 +37,10 @@ where } return; } + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + arg.make_indirect(); + return; + } let size = arg.layout.size; if size.bits() <= 128 { diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index d8db7ed6e4c0f..0938548222075 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -186,6 +186,11 @@ where // Not touching this... return; } + if is_arg && arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + int_regs = int_regs.saturating_sub(1); + arg.make_indirect(); + return; + } let mut cls_or_mem = classify_arg(cx, arg); if is_arg { diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index 8f8597ea662a8..85ee59eabc7c4 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -1,11 +1,14 @@ -use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size}; +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg}; use crate::spec::{HasTargetSpec, RustcAbi}; // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing -pub(crate) fn compute_abi_info(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { +pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Memory { sized: false } => {} @@ -59,6 +62,10 @@ pub(crate) fn compute_abi_info(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<' arg.make_indirect_from_ignore(); continue; } + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + arg.make_indirect(); + continue; + } fixup(arg, false); } // FIXME: We should likely also do something about ZST return types, similar to above. diff --git a/compiler/rustc_target/src/callconv/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs index a73a70a1a0c07..2b063b036a3af 100644 --- a/compiler/rustc_target/src/callconv/xtensa.rs +++ b/compiler/rustc_target/src/callconv/xtensa.rs @@ -15,7 +15,7 @@ const NUM_RET_GPRS: u64 = 4; const MAX_ARG_IN_REGS_SIZE: u64 = NUM_ARG_GPRS * 32; const MAX_RET_IN_REGS_SIZE: u64 = NUM_RET_GPRS * 32; -fn classify_ret_ty<'a, Ty, C>(arg: &mut ArgAbi<'_, Ty>) +fn classify_ret_ty<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, { @@ -26,7 +26,7 @@ where // The rules for return and argument types are the same, // so defer to `classify_arg_ty`. let mut arg_gprs_left = NUM_RET_GPRS; - classify_arg_ty(arg, &mut arg_gprs_left, MAX_RET_IN_REGS_SIZE); + classify_arg_ty(cx, arg, &mut arg_gprs_left, true); // Ret args cannot be passed via stack, we lower to indirect and let the backend handle the invisible reference match arg.mode { super::PassMode::Indirect { attrs: _, meta_attrs: _, ref mut on_stack } => { @@ -36,12 +36,24 @@ where } } -fn classify_arg_ty<'a, Ty, C>(arg: &mut ArgAbi<'_, Ty>, arg_gprs_left: &mut u64, max_size: u64) -where +fn classify_arg_ty<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + arg_gprs_left: &mut u64, + is_ret: bool, +) where Ty: TyAbiInterface<'a, C> + Copy, { assert!(*arg_gprs_left <= NUM_ARG_GPRS, "Arg GPR tracking underflow"); + let max_size = if is_ret { MAX_RET_IN_REGS_SIZE } else { MAX_ARG_IN_REGS_SIZE }; + + if !is_ret && arg.layout.pass_indirectly_in_non_rustic_abis(cx) { + *arg_gprs_left = arg_gprs_left.saturating_sub(1); + arg.make_indirect(); + return; + } + // Ignore empty structs/unions. if arg.layout.is_zst() { return; @@ -95,13 +107,13 @@ where } } -pub(crate) fn compute_abi_info<'a, Ty, C>(_cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { if !fn_abi.ret.is_ignore() { - classify_ret_ty(&mut fn_abi.ret); + classify_ret_ty(cx, &mut fn_abi.ret); } let mut arg_gprs_left = NUM_ARG_GPRS; @@ -110,7 +122,7 @@ where if arg.is_ignore() { continue; } - classify_arg_ty(arg, &mut arg_gprs_left, MAX_ARG_IN_REGS_SIZE); + classify_arg_ty(cx, arg, &mut arg_gprs_left, false); } } diff --git a/tests/ui/abi/pass-indirectly-attr.aarch64.stderr b/tests/ui/abi/pass-indirectly-attr.aarch64.stderr new file mode 100644 index 0000000000000..6fb4540cd055a --- /dev/null +++ b/tests/ui/abi/pass-indirectly-attr.aarch64.stderr @@ -0,0 +1,84 @@ +error: fn_abi_of(func) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: Type, + layout: Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(8 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-indirectly-attr.rs:28:1 + | +LL | pub extern "C" fn func(_: Type) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/pass-indirectly-attr.powerpc.stderr b/tests/ui/abi/pass-indirectly-attr.powerpc.stderr new file mode 100644 index 0000000000000..882f3133cd4ae --- /dev/null +++ b/tests/ui/abi/pass-indirectly-attr.powerpc.stderr @@ -0,0 +1,84 @@ +error: fn_abi_of(func) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: Type, + layout: Layout { + size: Size(4 bytes), + align: AbiAlign { + abi: Align(4 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(4 bytes), + pointee_align: Some( + Align(4 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-indirectly-attr.rs:28:1 + | +LL | pub extern "C" fn func(_: Type) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/pass-indirectly-attr.rs b/tests/ui/abi/pass-indirectly-attr.rs new file mode 100644 index 0000000000000..a8b7d3faf0b27 --- /dev/null +++ b/tests/ui/abi/pass-indirectly-attr.rs @@ -0,0 +1,31 @@ +//@ check-fail +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" +//@ add-core-stubs +//@ compile-flags: -O +// All architectures that have the attribute implemented: +//@ revisions: aarch64 powerpc s390x x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@ [aarch64] needs-llvm-components: aarch64 +//@ [powerpc] compile-flags: --target powerpc-unknown-linux-gnu +//@ [powerpc] needs-llvm-components: powerpc +//@ [s390x] compile-flags: --target s390x-unknown-linux-gnu +//@ [s390x] needs-llvm-components: systemz +//@ [x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [x86_64] needs-llvm-components: x86 +// FIXME: Add `xtensa` revision once `xtensa` support is fully available in upstream LLVM. + +#![feature(no_core, rustc_attrs)] +#![crate_type = "lib"] +#![no_core] +extern crate minicore; +use minicore::*; + +#[repr(C)] +#[rustc_pass_indirectly_in_non_rustic_abis] +pub struct Type(usize); + +#[rustc_abi(debug)] +pub extern "C" fn func(_: Type) {} +//~^ ERROR fn_abi_of(func) = FnAbi { +//~^^ ERROR mode: Indirect { +//~^^^ ERROR on_stack: false, diff --git a/tests/ui/abi/pass-indirectly-attr.s390x.stderr b/tests/ui/abi/pass-indirectly-attr.s390x.stderr new file mode 100644 index 0000000000000..6fb4540cd055a --- /dev/null +++ b/tests/ui/abi/pass-indirectly-attr.s390x.stderr @@ -0,0 +1,84 @@ +error: fn_abi_of(func) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: Type, + layout: Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(8 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-indirectly-attr.rs:28:1 + | +LL | pub extern "C" fn func(_: Type) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/pass-indirectly-attr.x86_64.stderr b/tests/ui/abi/pass-indirectly-attr.x86_64.stderr new file mode 100644 index 0000000000000..6fb4540cd055a --- /dev/null +++ b/tests/ui/abi/pass-indirectly-attr.x86_64.stderr @@ -0,0 +1,84 @@ +error: fn_abi_of(func) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: Type, + layout: Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(8 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-indirectly-attr.rs:28:1 + | +LL | pub extern "C" fn func(_: Type) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/pass-indirectly.aarch64.stderr b/tests/ui/attributes/pass-indirectly.aarch64.stderr new file mode 100644 index 0000000000000..30e54d97556c7 --- /dev/null +++ b/tests/ui/attributes/pass-indirectly.aarch64.stderr @@ -0,0 +1,11 @@ +error: `#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to `struct`s + --> $DIR/pass-indirectly.rs:15:1 + | +LL | #[rustc_pass_indirectly_in_non_rustic_abis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn not_a_struct() {} + | -------------------- is not a `struct` + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/pass-indirectly.rs b/tests/ui/attributes/pass-indirectly.rs new file mode 100644 index 0000000000000..65cc17baa648f --- /dev/null +++ b/tests/ui/attributes/pass-indirectly.rs @@ -0,0 +1,24 @@ +//@ check-fail +//@ revisions: x86_64 aarch64 +//@ add-core-stubs +//@ [x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [x86_64] needs-llvm-components: x86 +//@ [aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@ [aarch64] needs-llvm-components: aarch64 + +#![feature(no_core, rustc_attrs)] +#![crate_type = "lib"] +#![no_core] +extern crate minicore; +use minicore::*; + +#[rustc_pass_indirectly_in_non_rustic_abis] +//~^ ERROR: `#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to `struct`s +fn not_a_struct() {} + +#[repr(C)] +#[rustc_pass_indirectly_in_non_rustic_abis] +struct YesAStruct { + foo: u8, + bar: u16, +} diff --git a/tests/ui/attributes/pass-indirectly.x86_64.stderr b/tests/ui/attributes/pass-indirectly.x86_64.stderr new file mode 100644 index 0000000000000..30e54d97556c7 --- /dev/null +++ b/tests/ui/attributes/pass-indirectly.x86_64.stderr @@ -0,0 +1,11 @@ +error: `#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to `struct`s + --> $DIR/pass-indirectly.rs:15:1 + | +LL | #[rustc_pass_indirectly_in_non_rustic_abis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn not_a_struct() {} + | -------------------- is not a `struct` + +error: aborting due to 1 previous error + From 2bb2fc9cc707ee9e69e59574523510fadbaec61b Mon Sep 17 00:00:00 2001 From: beetrees Date: Fri, 4 Jul 2025 17:44:15 +0100 Subject: [PATCH 2/2] Rework `c_variadic` --- .../src/intrinsics/mod.rs | 2 +- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 2 - .../rustc_codegen_ssa/src/traits/intrinsic.rs | 4 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 4 - compiler/rustc_span/src/symbol.rs | 2 - library/core/src/ffi/mod.rs | 2 +- library/core/src/ffi/va_list.rs | 153 +++------- library/core/src/intrinsics/mod.rs | 13 +- library/std/src/ffi/mod.rs | 2 +- tests/auxiliary/rust_test_helpers.c | 4 + tests/codegen/cffi/c-variadic-copy.rs | 4 +- tests/codegen/cffi/c-variadic-opt.rs | 8 +- tests/codegen/cffi/c-variadic.rs | 2 +- .../c-link-to-rust-va-list-fn/checkrust.rs | 15 +- tests/ui/abi/variadic-ffi.rs | 32 +- .../pass-by-value-abi.aarch64.stderr | 94 ++++++ tests/ui/c-variadic/pass-by-value-abi.rs | 38 +++ .../pass-by-value-abi.x86_32.stderr | 89 ++++++ .../pass-by-value-abi.x86_64.stderr | 276 ++++++++++++++++++ tests/ui/c-variadic/variadic-ffi-4.rs | 16 +- tests/ui/c-variadic/variadic-ffi-4.stderr | 109 ++++--- .../mbe-dotdotdot-may-not-begin-a-type.rs | 2 +- .../variadic-ffi-semantic-restrictions.rs | 6 +- .../variadic-ffi-semantic-restrictions.stderr | 6 +- 24 files changed, 649 insertions(+), 236 deletions(-) create mode 100644 tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr create mode 100644 tests/ui/c-variadic/pass-by-value-abi.rs create mode 100644 tests/ui/c-variadic/pass-by-value-abi.x86_32.stderr create mode 100644 tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 4ff5773a06cb2..e3bc3b64e2259 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1450,7 +1450,7 @@ fn codegen_regular_intrinsic_call<'tcx>( } // FIXME implement variadics in cranelift - sym::va_copy | sym::va_arg | sym::va_end => { + sym::va_copy | sym::va_arg => { fx.tcx.dcx().span_fatal( source_info.span, "Defining variadic functions is not yet supported by Cranelift", diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index fc95f62b4a43d..cb4e020abcb23 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -131,8 +131,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } - sym::va_start => bx.va_start(args[0].immediate()), - sym::va_end => bx.va_end(args[0].immediate()), sym::size_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 7d0c6be4c650d..5b755469fc114 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -30,10 +30,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { vtable_byte_offset: u64, typeid: Self::Metadata, ) -> Self::Value; - /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in + /// Trait method used to inject `va_start` on the "spoofed" `VaList` in /// Rust defined C-variadic functions. fn va_start(&mut self, val: Self::Value) -> Self::Value; - /// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before + /// Trait method used to inject `va_end` on the "spoofed" `VaList` before /// Rust defined C-variadic functions return. fn va_end(&mut self, val: Self::Value) -> Self::Value; } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cebf7d1b5324e..d2494425b5b11 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -506,10 +506,6 @@ pub(crate) fn check_intrinsic_type( ) } - sym::va_start | sym::va_end => { - (0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit) - } - sym::va_copy => { let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not); let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a74140119dda6..2bd0765bc362a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2310,9 +2310,7 @@ symbols! { v8plus, va_arg, va_copy, - va_end, va_list, - va_start, val, validity, values, diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 0bc98e2ea8645..2b78b36c59c81 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -28,7 +28,7 @@ pub mod c_str; issue = "44930", reason = "the `c_variadic` feature has not been properly tested on all supported platforms" )] -pub use self::va_list::{VaArgSafe, VaList, VaListImpl}; +pub use self::va_list::{VaArgSafe, VaList}; #[unstable( feature = "c_variadic", diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 88ad11977774d..8f0b365520f01 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -5,13 +5,27 @@ use crate::ffi::c_void; #[allow(unused_imports)] use crate::fmt; -use crate::intrinsics::{va_arg, va_copy, va_end}; -use crate::marker::{PhantomData, PhantomInvariantLifetime}; -use crate::ops::{Deref, DerefMut}; +use crate::intrinsics::{va_arg, va_copy}; +use crate::marker::PhantomInvariantLifetime; -// The name is WIP, using `VaListImpl` for now. -// // Most targets explicitly specify the layout of `va_list`, this layout is matched here. +// For `va_list`s which are single-element array in C (and therefore experience array-to-pointer +// decay when passed as arguments in C), the `VaList` struct is annotated with +// `#[rustc_pass_indirectly_in_non_rustic_abis]`. This ensures that the compiler uses the correct +// ABI for functions like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly. + +// Note that currently support for `#[rustc_pass_indirectly_in_non_rustic_abis]` is only implemented +// on architectures which need it here, so when adding support for a new architecture the following +// will need to happen: +// +// - Check that the calling conventions used on the new architecture correctly check +// `arg.layout.pass_indirectly_in_non_rustic_abis()` and call `arg.make_indirect()` if it returns +// `true`. +// - Add a revision to the `tests/ui/abi/pass-indirectly-attr.rs` test for the new architecture. +// - Add the new architecture to the `supported_architectures` array in the +// `check_pass_indirectly_in_non_rustic_abis` function in +// `compiler/rustc_passes/src/check_attr.rs`. This will stop the compiler from emitting an error +// message when the attribute is used on that architecture. crate::cfg_select! { all( target_arch = "aarch64", @@ -27,7 +41,8 @@ crate::cfg_select! { #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 #[derive(Debug)] #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + pub struct VaList<'f> { stack: *mut c_void, gr_top: *mut c_void, vr_top: *mut c_void, @@ -41,7 +56,8 @@ crate::cfg_select! { #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 #[derive(Debug)] #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + pub struct VaList<'f> { gpr: u8, fpr: u8, reserved: u16, @@ -55,7 +71,8 @@ crate::cfg_select! { #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 #[derive(Debug)] #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + pub struct VaList<'f> { gpr: i64, fpr: i64, overflow_arg_area: *mut c_void, @@ -68,7 +85,8 @@ crate::cfg_select! { #[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 #[derive(Debug)] #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + pub struct VaList<'f> { gp_offset: i32, fp_offset: i32, overflow_arg_area: *mut c_void, @@ -81,7 +99,8 @@ crate::cfg_select! { #[repr(C)] #[derive(Debug)] #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + pub struct VaList<'f> { stk: *mut i32, reg: *mut i32, ndx: i32, @@ -94,7 +113,7 @@ crate::cfg_select! { // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599) // - windows // - uefi - // - any other target for which we don't specify the `VaListImpl` above + // - any other target for which we don't specify the `VaList` above // // In this implementation the `va_list` type is just an alias for an opaque pointer. // That pointer is probably just the next variadic argument on the caller's stack. @@ -102,89 +121,22 @@ crate::cfg_select! { /// Basic implementation of a `va_list`. #[repr(transparent)] #[lang = "va_list"] - pub struct VaListImpl<'f> { + pub struct VaList<'f> { ptr: *mut c_void, - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to + // Invariant over `'f`, so each `VaList<'f>` object is tied to // the region of the function it's defined in _marker: PhantomInvariantLifetime<'f>, } - impl<'f> fmt::Debug for VaListImpl<'f> { + impl<'f> fmt::Debug for VaList<'f> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) + f.debug_tuple("VaList").field(&self.ptr).finish() } } } } -crate::cfg_select! { - all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - not(target_arch = "xtensa"), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), - ) => { - /// A wrapper for a `va_list` - #[repr(transparent)] - #[derive(Debug)] - pub struct VaList<'a, 'f: 'a> { - inner: &'a mut VaListImpl<'f>, - _marker: PhantomData<&'a mut VaListImpl<'f>>, - } - - - impl<'f> VaListImpl<'f> { - /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } - } - } - } - - _ => { - /// A wrapper for a `va_list` - #[repr(transparent)] - #[derive(Debug)] - pub struct VaList<'a, 'f: 'a> { - inner: VaListImpl<'f>, - _marker: PhantomData<&'a mut VaListImpl<'f>>, - } - - impl<'f> VaListImpl<'f> { - /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } - } - } - } -} - -impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { - type Target = VaListImpl<'f>; - - #[inline] - fn deref(&self) -> &VaListImpl<'f> { - &self.inner - } -} - -impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { - #[inline] - fn deref_mut(&mut self) -> &mut VaListImpl<'f> { - &mut self.inner - } -} - mod sealed { pub trait Sealed {} @@ -202,7 +154,7 @@ mod sealed { impl Sealed for *const T {} } -/// Trait which permits the allowed types to be used with [`VaListImpl::arg`]. +/// Trait which permits the allowed types to be used with [`VaList::arg`]. /// /// # Safety /// @@ -232,34 +184,20 @@ unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *mut T {} unsafe impl VaArgSafe for *const T {} -impl<'f> VaListImpl<'f> { +impl<'f> VaList<'f> { /// Advance to the next arg. #[inline] pub unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } - - /// Copies the `va_list` at the current location. - pub unsafe fn with_copy(&self, f: F) -> R - where - F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, - { - let mut ap = self.clone(); - let ret = f(ap.as_va_list()); - // SAFETY: the caller must uphold the safety contract for `va_end`. - unsafe { - va_end(&mut ap); - } - ret - } } -impl<'f> Clone for VaListImpl<'f> { +impl<'f> Clone for VaList<'f> { #[inline] fn clone(&self) -> Self { let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal + // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal. unsafe { va_copy(dest.as_mut_ptr(), self); dest.assume_init() @@ -267,17 +205,10 @@ impl<'f> Clone for VaListImpl<'f> { } } -impl<'f> Drop for VaListImpl<'f> { +impl<'f> Drop for VaList<'f> { fn drop(&mut self) { - // FIXME: this should call `va_end`, but there's no clean way to - // guarantee that `drop` always gets inlined into its caller, - // so the `va_end` would get directly called from the same function as - // the corresponding `va_copy`. `man va_end` states that C requires this, - // and LLVM basically follows the C semantics, so we need to make sure - // that `va_end` is always called from the same function as `va_copy`. - // For more details, see https://github.com/rust-lang/rust/pull/59625 - // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. - // - // This works for now, since `va_end` is a no-op on all current LLVM targets. + // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour + // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this + // destructor is empty. } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 791d10eda6d0d..a95b3bbb618e2 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -54,7 +54,7 @@ )] #![allow(missing_docs)] -use crate::ffi::va_list::{VaArgSafe, VaListImpl}; +use crate::ffi::va_list::{VaArgSafe, VaList}; use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple}; use crate::ptr; @@ -3149,7 +3149,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// FIXME: document safety requirements #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); +pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>); /// Loads an argument of type `T` from the `va_list` `ap` and increment the /// argument `ap` points to. @@ -3157,11 +3157,4 @@ pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); /// FIXME: document safety requirements #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; - -/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. -/// -/// FIXME: document safety requirements -#[rustc_intrinsic] -#[rustc_nounwind] -pub unsafe fn va_end(ap: &mut VaListImpl<'_>); +pub unsafe fn va_arg(ap: &mut VaList<'_>) -> T; diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index f44e12d48addf..999bd5e63dc45 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -172,7 +172,7 @@ pub use core::ffi::c_void; all supported platforms", issue = "44930" )] -pub use core::ffi::{VaArgSafe, VaList, VaListImpl}; +pub use core::ffi::{VaArgSafe, VaList}; #[stable(feature = "core_ffi_c", since = "1.64.0")] pub use core::ffi::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, diff --git a/tests/auxiliary/rust_test_helpers.c b/tests/auxiliary/rust_test_helpers.c index 34cc7fd5dfbed..cd10d6b98ca7b 100644 --- a/tests/auxiliary/rust_test_helpers.c +++ b/tests/auxiliary/rust_test_helpers.c @@ -314,6 +314,10 @@ double rust_interesting_average(uint64_t n, ...) { return sum; } +int32_t rust_va_list_next_i32(va_list* ap) { + return va_arg(*ap, int32_t); +} + int32_t rust_int8_to_int32(int8_t x) { return (int32_t)x; } diff --git a/tests/codegen/cffi/c-variadic-copy.rs b/tests/codegen/cffi/c-variadic-copy.rs index 4c61c4fcf68d3..0cbdcb4bbb85c 100644 --- a/tests/codegen/cffi/c-variadic-copy.rs +++ b/tests/codegen/cffi/c-variadic-copy.rs @@ -1,4 +1,4 @@ -// Tests that `VaListImpl::clone` gets inlined into a call to `llvm.va_copy` +// Tests that `VaList::clone` gets inlined into a call to `llvm.va_copy` #![crate_type = "lib"] #![feature(c_variadic)] @@ -12,5 +12,5 @@ extern "C" { pub unsafe extern "C" fn clone_variadic(ap: VaList) { let mut ap2 = ap.clone(); // CHECK: call void @llvm.va_copy - foreign_c_variadic_1(ap2.as_va_list(), 42i32); + foreign_c_variadic_1(ap2, 42i32); } diff --git a/tests/codegen/cffi/c-variadic-opt.rs b/tests/codegen/cffi/c-variadic-opt.rs index 7e544ee7f37da..3cc0c3e9f9bdd 100644 --- a/tests/codegen/cffi/c-variadic-opt.rs +++ b/tests/codegen/cffi/c-variadic-opt.rs @@ -10,21 +10,21 @@ extern "C" { } // Ensure that `va_start` and `va_end` are properly injected even -// when the "spoofed" `VaListImpl` is not used. +// when the "spoofed" `VaList` is not used. #[no_mangle] pub unsafe extern "C" fn c_variadic_no_use(fmt: *const i8, mut ap: ...) -> i32 { // CHECK: call void @llvm.va_start - vprintf(fmt, ap.as_va_list()) + vprintf(fmt, ap) // CHECK: call void @llvm.va_end } -// Check that `VaListImpl::clone` gets inlined into a direct call to `llvm.va_copy` +// Check that `VaList::clone` gets inlined into a direct call to `llvm.va_copy` #[no_mangle] pub unsafe extern "C" fn c_variadic_clone(fmt: *const i8, mut ap: ...) -> i32 { // CHECK: call void @llvm.va_start let mut ap2 = ap.clone(); // CHECK: call void @llvm.va_copy - let res = vprintf(fmt, ap2.as_va_list()); + let res = vprintf(fmt, ap2); res // CHECK: call void @llvm.va_end } diff --git a/tests/codegen/cffi/c-variadic.rs b/tests/codegen/cffi/c-variadic.rs index 140d2f37f4693..46d85121c2333 100644 --- a/tests/codegen/cffi/c-variadic.rs +++ b/tests/codegen/cffi/c-variadic.rs @@ -25,7 +25,7 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() { } // Ensure that we do not remove the `va_list` passed to the foreign function when -// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics. +// removing the "spoofed" `VaList` that is used by Rust defined C-variadics. pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) { // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap) foreign_c_variadic_1(ap); diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index 63d8d713d6226..f6aece164e62b 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -2,7 +2,7 @@ #![feature(c_variadic)] #![feature(cfg_select)] -use std::ffi::{CStr, CString, VaList, VaListImpl, c_char, c_double, c_int, c_long, c_longlong}; +use std::ffi::{CStr, CString, VaList, VaList, c_char, c_double, c_int, c_long, c_longlong}; macro_rules! continue_if { ($cond:expr) => { @@ -58,11 +58,8 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize { continue_if!(ap.arg::() == 16); continue_if!(ap.arg::() == 'A' as c_int); continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Skip Me!")); - ap.with_copy( - |mut ap| { - if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff } - }, - ) + let mut ap = ap.clone(); + if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff } } #[unsafe(no_mangle)] @@ -153,8 +150,8 @@ pub unsafe extern "C" fn check_varargs_5(_: c_int, mut ap: ...) -> usize { unsafe extern "C" { fn test_variadic(_: c_int, ...) -> usize; fn test_va_list_by_value(_: VaList) -> usize; - fn test_va_list_by_pointer(_: *mut VaListImpl) -> usize; - fn test_va_list_by_pointer_pointer(_: *mut *mut VaListImpl) -> usize; + fn test_va_list_by_pointer(_: *mut VaList) -> usize; + fn test_va_list_by_pointer_pointer(_: *mut *mut VaList) -> usize; } #[unsafe(no_mangle)] @@ -165,7 +162,7 @@ extern "C" fn run_test_variadic() -> usize { #[unsafe(no_mangle)] extern "C" fn run_test_va_list_by_value() -> usize { unsafe extern "C" fn helper(mut ap: ...) -> usize { - unsafe { test_va_list_by_value(ap.as_va_list()) } + unsafe { test_va_list_by_value(ap) } } unsafe { helper(1 as c_longlong, 2 as c_int, 3 as c_longlong) } diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index 6cfae0f2a320f..fcf8976420357 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -8,37 +8,45 @@ extern "C" { fn rust_interesting_average(_: u64, ...) -> f64; fn rust_valist_interesting_average(_: u64, _: VaList) -> f64; + + fn rust_va_list_next_i32(_: *mut VaList<'_>) -> i32; } -pub unsafe extern "C" fn test_valist_forward(n: u64, mut ap: ...) -> f64 { - rust_valist_interesting_average(n, ap.as_va_list()) +pub unsafe extern "C" fn test_valist_forward(n: u64, ap: ...) -> f64 { + rust_valist_interesting_average(n, ap) } -pub unsafe extern "C-unwind" fn c_unwind_can_forward(n: u64, mut ap: ...) -> f64 { - rust_valist_interesting_average(n, ap.as_va_list()) +pub unsafe extern "C-unwind" fn c_unwind_can_forward(n: u64, ap: ...) -> f64 { + rust_valist_interesting_average(n, ap) } pub unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) { - let mut ap2 = ap.clone(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 30); + let ap2 = ap.clone(); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 30); // Advance one pair in the copy before checking let mut ap2 = ap.clone(); let _ = ap2.arg::(); let _ = ap2.arg::(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 50); // Advance one pair in the original let _ = ap.arg::(); let _ = ap.arg::(); - let mut ap2 = ap.clone(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50); + let ap2 = ap.clone(); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 50); let mut ap2 = ap.clone(); let _ = ap2.arg::(); let _ = ap2.arg::(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 70); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 70); +} + +pub unsafe extern "C" fn test_ref(mut ap: ...) { + assert_eq!(rust_va_list_next_i32(&mut ap), 2); + assert_eq!(rust_va_list_next_i32(&mut ap), 4); + assert_eq!(rust_va_list_next_i32(&mut ap), 8); } pub fn main() { @@ -83,4 +91,8 @@ pub fn main() { unsafe { test_va_copy(4, 10i64, 10f64, 20i64, 20f64, 30i64, 30f64, 40i64, 40f64); } + + unsafe { + test_ref(2, 4, 8); + } } diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr new file mode 100644 index 0000000000000..7df56402cb740 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -0,0 +1,94 @@ +error: fn_abi_of(take_va_list) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(32 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + Size(8 bytes), + Size(16 bytes), + Size(24 bytes), + Size(28 bytes), + Size(32 bytes), + ], + memory_index: [ + 0, + 1, + 2, + 3, + 4, + 5, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(32 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:18:1 + | +LL | pub extern "C" fn take_va_list(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/c-variadic/pass-by-value-abi.rs b/tests/ui/c-variadic/pass-by-value-abi.rs new file mode 100644 index 0000000000000..768bd53f1f632 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.rs @@ -0,0 +1,38 @@ +//@ check-fail +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" +//@ revisions: x86_64 aarch64 x86_32 +//@ compile-flags: -O +//@ [x86_64] only-x86_64 +//@ [aarch64] only-aarch64 +// x86_32 dosen't use `#[rustc_pass_indirectly_in_non_rustic_abis]` and is tested in CI, so is here +// for comparison. +//@ [x86_32] only-x86 + +#![feature(rustc_attrs, c_variadic)] +#![crate_type = "lib"] + +// Can't use `minicore` here as this is testing the implementation in `core::ffi` specifically. +use std::ffi::VaList; + +#[rustc_abi(debug)] +pub extern "C" fn take_va_list(_: VaList<'_>) {} +//~^ ERROR fn_abi_of(take_va_list) = FnAbi { +//[x86_64]~^^ ERROR mode: Indirect { +//[x86_64]~^^^ ERROR on_stack: false, +//[aarch64]~^^^^ ERROR mode: Indirect { +//[aarch64]~^^^^^ ERROR on_stack: false, +//[x86_32]~^^^^^^ ERROR mode: Direct( + +#[cfg(target_arch = "x86_64")] +#[rustc_abi(debug)] +pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {} +//[x86_64]~^ ERROR fn_abi_of(take_va_list_sysv64) = FnAbi { +//[x86_64]~^^ ERROR mode: Indirect { +//[x86_64]~^^^ ERROR on_stack: false, + +#[cfg(target_arch = "x86_64")] +#[rustc_abi(debug)] +pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {} +//[x86_64]~^ ERROR: fn_abi_of(take_va_list_win64) = FnAbi { +//[x86_64]~^^ ERROR mode: Indirect { +//[x86_64]~^^^ ERROR on_stack: false, diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_32.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_32.stderr new file mode 100644 index 0000000000000..601c2563e7922 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_32.stderr @@ -0,0 +1,89 @@ +error: fn_abi_of(take_va_list) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(4 bytes), + align: AbiAlign { + abi: Align(4 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 0..=4294967295, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + Size(4 bytes), + ], + memory_index: [ + 0, + 1, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, + }, + }, + mode: Direct( + ArgAttributes { + regular: NoUndef, + arg_ext: None, + pointee_size: Size(0 bytes), + pointee_align: None, + }, + ), + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:18:1 + | +LL | pub extern "C" fn take_va_list(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr new file mode 100644 index 0000000000000..b30f297fe2e94 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -0,0 +1,276 @@ +error: fn_abi_of(take_va_list) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(24 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + Size(4 bytes), + Size(8 bytes), + Size(16 bytes), + Size(24 bytes), + ], + memory_index: [ + 0, + 1, + 2, + 3, + 4, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(24 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:18:1 + | +LL | pub extern "C" fn take_va_list(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: fn_abi_of(take_va_list_sysv64) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(24 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + Size(4 bytes), + Size(8 bytes), + Size(16 bytes), + Size(24 bytes), + ], + memory_index: [ + 0, + 1, + 2, + 3, + 4, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(24 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: X86( + SysV64, + ), + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:28:1 + | +LL | pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: fn_abi_of(take_va_list_win64) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(24 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + Size(4 bytes), + Size(8 bytes), + Size(16 bytes), + Size(24 bytes), + ], + memory_index: [ + 0, + 1, + 2, + 3, + 4, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: NoAlias | NoCapture | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(24 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: X86( + Win64, + ), + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:35:1 + | +LL | pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/c-variadic/variadic-ffi-4.rs b/tests/ui/c-variadic/variadic-ffi-4.rs index 8064037942259..bf25a35d75e84 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.rs +++ b/tests/ui/c-variadic/variadic-ffi-4.rs @@ -2,36 +2,32 @@ #![no_std] #![feature(c_variadic)] -use core::ffi::{VaList, VaListImpl}; +use core::ffi::VaList; -pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { +pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> { ap //~^ ERROR: lifetime may not live long enough //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { +pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> { ap //~ ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) { - let _ = ap.with_copy(|ap| ap); //~ ERROR: lifetime may not live long enough -} - -pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) { *ap0 = ap1; //~^ ERROR: lifetime may not live long enough //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { ap0 = &mut ap1; //~^ ERROR: `ap1` does not live long enough //~| ERROR: lifetime may not live long enough //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) { *ap0 = ap1.clone(); //~^ ERROR: lifetime may not live long enough //~| ERROR: lifetime may not live long enough diff --git a/tests/ui/c-variadic/variadic-ffi-4.stderr b/tests/ui/c-variadic/variadic-ffi-4.stderr index fc9f8036083a6..ddbe5693d4010 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.stderr +++ b/tests/ui/c-variadic/variadic-ffi-4.stderr @@ -1,113 +1,104 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:8:5 | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `VaListImpl<'1>` +LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> { + | -- -- has type `VaList<'1>` | | | lifetime `'f` defined here LL | ap | ^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'f` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:8:5 | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `VaListImpl<'1>` +LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> { + | -- -- has type `VaList<'1>` | | | lifetime `'f` defined here LL | ap | ^^ function was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:14:5 | -LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { - | -- has type `VaListImpl<'1>` +LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> { + | -- has type `VaList<'1>` LL | ap | ^^ returning this value requires that `'1` must outlive `'static` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:18:31 + --> $DIR/variadic-ffi-4.rs:18:5 | -LL | let _ = ap.with_copy(|ap| ap); - | --- ^^ returning this value requires that `'1` must outlive `'2` - | | | - | | return type of closure is VaList<'2, '_> - | has type `VaList<'1, '_>` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:22:5 - | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | *ap0 = ap1; | ^^^^ assignment requires that `'1` must outlive `'2` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:22:5 + --> $DIR/variadic-ffi-4.rs:18:5 | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | *ap0 = ap1; | ^^^^ assignment requires that `'2` must outlive `'1` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:28:5 + --> $DIR/variadic-ffi-4.rs:24:5 | -LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` | - = note: requirement occurs because of a mutable reference to `VaListImpl<'_>` + = note: requirement occurs because of a mutable reference to `VaList<'_>` = note: mutable references are invariant over their type parameter = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:28:5 + --> $DIR/variadic-ffi-4.rs:24:5 | -LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` | - = note: requirement occurs because of a mutable reference to `VaListImpl<'_>` + = note: requirement occurs because of a mutable reference to `VaList<'_>` = note: mutable references are invariant over their type parameter = help: see for more information about variance error[E0597]: `ap1` does not live long enough - --> $DIR/variadic-ffi-4.rs:28:11 + --> $DIR/variadic-ffi-4.rs:24:11 | -LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | - ------- binding `ap1` declared here +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | - ------- binding `ap1` declared here | | | let's call the lifetime of this reference `'3` LL | ap0 = &mut ap1; @@ -120,33 +111,33 @@ LL | } | - `ap1` dropped here while still borrowed error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:35:5 + --> $DIR/variadic-ffi-4.rs:31:5 | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | *ap0 = ap1.clone(); | ^^^^ assignment requires that `'2` must outlive `'1` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:35:12 + --> $DIR/variadic-ffi-4.rs:31:12 | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | *ap0 = ap1.clone(); | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` + = note: requirement occurs because of the type `VaList<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaList<'f>` is invariant over the parameter `'f` = help: see for more information about variance -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs b/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs index 8be99f22d2ee0..b29f6915ae3d6 100644 --- a/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs +++ b/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs @@ -1,4 +1,4 @@ -// A bare `...` represents `CVarArgs` (`VaListImpl<'_>`) in function argument type +// A bare `...` represents `CVarArgs` (`VaList<'_>`) in function argument type // position without being a proper type syntactically. // This test ensures that we do not regress certain MBE calls would we ever promote // `...` to a proper type syntactically. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 1cd6d13d56b6b..2652361e03a02 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -31,12 +31,12 @@ extern "C" fn f3_3(..., x: isize) {} const unsafe extern "C" fn f4_1(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time const extern "C" fn f4_2(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic //~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg -//~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(..., x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic @@ -64,7 +64,7 @@ impl X { const fn i_f5(x: isize, ...) {} //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg //~| ERROR functions cannot be both `const` and C-variadic - //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time + //~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time } trait T { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index b740cef020055..44bb61d5baf9c 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -201,7 +201,7 @@ error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` function LL | fn t_f6(..., x: isize); | ^^^ -error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:32:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} @@ -209,7 +209,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | | | the destructor for this type cannot be evaluated in constant functions -error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} @@ -217,7 +217,7 @@ LL | const extern "C" fn f4_2(x: isize, ...) {} | | | the destructor for this type cannot be evaluated in constant functions -error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 | LL | const fn i_f5(x: isize, ...) {}