Auto merge of #148472 - Zalathar:rollup-zwlz09o, r=Zalathar

Rollup of 4 pull requests

Successful merges:

 - rust-lang/rust#144529 (Add `#[rustc_pass_indirectly_in_non_rustic_abis]`)
 - rust-lang/rust#147017 (FCW for repr(C) enums whose discriminant values do not fit into a c_int or c_uint)
 - rust-lang/rust#148459 (bootstrap: Split out a separate `./x test bootstrap-py` step)
 - rust-lang/rust#148468 (add logging to `fudge_inference_if_ok`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-11-04 12:09:10 +00:00
commit e5efc33672
61 changed files with 1026 additions and 96 deletions

View file

@ -812,7 +812,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let (max, min) = largest_niche
// We might have no inhabited variants, so pretend there's at least one.
.unwrap_or((0, 0));
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::discr_range_of_repr(tcx, ty, &repr, min, max);
let mut align = dl.aggregate_align;
let mut max_repr_align = repr.align;

View file

@ -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).
///
/// This is used to replicate some of the behaviour of C array-to-pointer decay; however unlike
/// C any changes the caller makes to the passed value will not be reflected in the callee, so
/// the attribute is only useful for types where observing the value in the caller after the
/// function call isn't allowed (a.k.a. `va_list`).
///
/// This function handles transparent types automatically.
pub fn pass_indirectly_in_non_rustic_abis<C>(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<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>

View file

@ -88,14 +88,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();
@ -183,6 +186,11 @@ impl ReprOptions {
/// Returns the discriminant type, given these `repr` options.
/// This must only be called on enums!
///
/// This is the "typeck type" of the discriminant, which is effectively the maximum size:
/// discriminant values will be wrapped to fit (with a lint). Layout can later decide to use a
/// smaller type for the tag that stores the discriminant at runtime and that will work just
/// fine, it just induces casts when getting/setting the discriminant.
pub fn discr_type(&self) -> IntegerType {
self.int.unwrap_or(IntegerType::Pointer(true))
}

View file

@ -676,3 +676,12 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
}
}
pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
}

View file

@ -20,8 +20,9 @@ use crate::attributes::allow_unstable::{
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser,
TargetFeatureParser, TrackCallerParser, UsedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
@ -243,6 +244,7 @@ attribute_parsers!(
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,

View file

@ -657,6 +657,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-naked-attribute"),
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!(

View file

@ -679,6 +679,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
RustcPassIndirectlyInNonRusticAbis(Span),
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),

View file

@ -91,6 +91,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcMain => No,
RustcObjectLifetimeDefault => No,
RustcPassIndirectlyInNonRusticAbis(..) => No,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
Sanitize { .. } => No,
ShouldPanic { .. } => No,

View file

@ -782,7 +782,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
tcx.ensure_ok().generics_of(def_id);
tcx.ensure_ok().type_of(def_id);
tcx.ensure_ok().predicates_of(def_id);
crate::collect::lower_enum_variant_types(tcx, def_id.to_def_id());
crate::collect::lower_enum_variant_types(tcx, def_id);
check_enum(tcx, def_id);
check_variances_for_type_defn(tcx, def_id);
}

View file

@ -19,7 +19,7 @@ use std::cell::Cell;
use std::iter;
use std::ops::Bound;
use rustc_abi::ExternAbi;
use rustc_abi::{ExternAbi, Size};
use rustc_ast::Recovered;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::unord::UnordMap;
@ -605,32 +605,70 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {
tcx.ensure_ok().predicates_of(def_id);
}
pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id);
let repr_type = def.repr().discr_type();
let initial = repr_type.initial_discriminant(tcx);
let mut prev_discr = None::<Discr<'_>>;
// Some of the logic below relies on `i128` being able to hold all c_int and c_uint values.
assert!(tcx.sess.target.c_int_width < 128);
let mut min_discr = i128::MAX;
let mut max_discr = i128::MIN;
// fill the discriminant values and field types
for variant in def.variants() {
let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx));
prev_discr = Some(
if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr {
def.eval_explicit_discr(tcx, const_def_id).ok()
} else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
Some(discr)
} else {
let cur_discr = if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr {
def.eval_explicit_discr(tcx, const_def_id).ok()
} else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
Some(discr)
} else {
let span = tcx.def_span(variant.def_id);
tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed {
span,
discr: prev_discr.unwrap().to_string(),
item_name: tcx.item_ident(variant.def_id),
wrapped_discr: wrapped_discr.to_string(),
});
None
}
.unwrap_or(wrapped_discr);
if def.repr().c() {
let c_int = Size::from_bits(tcx.sess.target.c_int_width);
let c_uint_max = i128::try_from(c_int.unsigned_int_max()).unwrap();
// c_int is a signed type, so get a proper signed version of the discriminant
let discr_size = cur_discr.ty.int_size_and_signed(tcx).0;
let discr_val = discr_size.sign_extend(cur_discr.val);
min_discr = min_discr.min(discr_val);
max_discr = max_discr.max(discr_val);
// The discriminant range must either fit into c_int or c_uint.
if !(min_discr >= c_int.signed_int_min() && max_discr <= c_int.signed_int_max())
&& !(min_discr >= 0 && max_discr <= c_uint_max)
{
let span = tcx.def_span(variant.def_id);
tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed {
let msg = if discr_val < c_int.signed_int_min() || discr_val > c_uint_max {
"`repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`"
} else if discr_val < 0 {
"`repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`"
} else {
"`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`"
};
tcx.node_span_lint(
rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT,
tcx.local_def_id_to_hir_id(def_id),
span,
discr: prev_discr.unwrap().to_string(),
item_name: tcx.item_ident(variant.def_id),
wrapped_discr: wrapped_discr.to_string(),
});
None
|d| {
d.primary_message(msg)
.note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C")
.help("use `repr($int_ty)` instead to explicitly set the size of this enum");
}
);
}
.unwrap_or(wrapped_discr),
);
}
prev_discr = Some(cur_discr);
for f in &variant.fields {
tcx.ensure_ok().generics_of(f.did);

View file

@ -1,3 +1,4 @@
use std::fmt::Debug;
use std::ops::Range;
use rustc_data_structures::{snapshot_vec as sv, unify as ut};
@ -84,11 +85,12 @@ impl<'tcx> InferCtxt<'tcx> {
/// the actual types (`?T`, `Option<?T>`) -- and remember that
/// after the snapshot is popped, the variable `?T` is no longer
/// unified.
#[instrument(skip(self, f), level = "debug")]
#[instrument(skip(self, f), level = "debug", ret)]
pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
T: TypeFoldable<TyCtxt<'tcx>>,
E: Debug,
{
let variable_lengths = self.variable_lengths();
let (snapshot_vars, value) = self.probe(|_| {

View file

@ -10,7 +10,7 @@ use rustc_span::{Span, Symbol, sym};
use tracing::debug;
use {rustc_ast as ast, rustc_hir as hir};
mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations
mod improper_ctypes; // these files do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations
pub(crate) use improper_ctypes::ImproperCTypesLint;
use crate::lints::{
@ -25,7 +25,6 @@ use crate::lints::{
use crate::{LateContext, LateLintPass, LintContext};
mod literal;
use literal::{int_ty_range, lint_literal, uint_ty_range};
declare_lint! {

View file

@ -86,6 +86,7 @@ declare_lint_pass! {
REFINING_IMPL_TRAIT_INTERNAL,
REFINING_IMPL_TRAIT_REACHABLE,
RENAMED_AND_REMOVED_LINTS,
REPR_C_ENUMS_LARGER_THAN_INT,
REPR_TRANSPARENT_NON_ZST_FIELDS,
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
@ -5213,3 +5214,52 @@ declare_lint! {
Warn,
r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#,
}
declare_lint! {
/// The `repr_c_enums_larger_than_int` lint detects `repr(C)` enums with discriminant
/// values that do not fit into a C `int` or `unsigned int`.
///
/// ### Example
///
/// ```rust,ignore (only errors on 64bit)
/// #[repr(C)]
/// enum E {
/// V = 9223372036854775807, // i64::MAX
/// }
/// ```
///
/// This will produce:
///
/// ```text
/// error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
/// --> $DIR/repr-c-big-discriminant1.rs:16:5
/// |
/// LL | A = 9223372036854775807, // i64::MAX
/// | ^
/// |
/// = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
/// = help: use `repr($int_ty)` instead to explicitly set the size of this enum
/// ```
///
/// ### Explanation
///
/// In C, enums with discriminants that do not all fit into an `int` or all fit into an
/// `unsigned int` are a portability hazard: such enums are only permitted since C23, and not
/// supported e.g. by MSVC.
///
/// Furthermore, Rust interprets the discriminant values of `repr(C)` enums as expressions of
/// type `isize`. This makes it impossible to implement the C23 behavior of enums where the enum
/// discriminants have no predefined type and instead the enum uses a type large enough to hold
/// all discriminants.
///
/// Therefore, `repr(C)` enums in Rust require that either all discriminants to fit into a C
/// `int` or they all fit into an `unsigned int`.
pub REPR_C_ENUMS_LARGER_THAN_INT,
Warn,
"repr(C) enums with discriminant values that do not fit into a C int",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #124403 <https://github.com/rust-lang/rust/issues/124403>",
report_in_deps: false,
};
}

View file

@ -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;
@ -72,7 +72,10 @@ impl abi::Integer {
/// signed discriminant range and `#[repr]` attribute.
/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
/// that shouldn't affect anything, other than maybe debuginfo.
fn repr_discr<'tcx>(
///
/// This is the basis for computing the type of the *tag* of an enum (which can be smaller than
/// the type of the *discriminant*, which is determined by [`ReprOptions::discr_type`]).
fn discr_range_of_repr<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
repr: &ReprOptions,
@ -108,7 +111,8 @@ impl abi::Integer {
abi::Integer::I8
};
// Pick the smallest fit.
// Pick the smallest fit. Prefer unsigned; that matches clang in cases where this makes a
// difference (https://godbolt.org/z/h4xEasW1d) so it is crucial for repr(C).
if unsigned_fit <= signed_fit {
(cmp::max(unsigned_fit, at_least), false)
} else {
@ -1173,6 +1177,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.

View file

@ -1573,6 +1573,14 @@ impl<'tcx> TyCtxt<'tcx> {
flags.insert(ReprFlags::IS_LINEAR);
}
// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
if 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 }
}

View file

@ -284,6 +284,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::RustcCoherenceIsCore(..)
| AttributeKind::DebuggerVisualizer(..)
| AttributeKind::RustcMain
| AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
| AttributeKind::PinV2(..),
) => { /* do nothing */ }
Attribute::Unparsed(attr_item) => {
@ -1770,6 +1771,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
target: target.to_string(),
});
}
// Error on `#[repr(transparent)]` in combination with
// `#[rustc_pass_indirectly_in_non_rustic_abis]`
if is_transparent
&& let Some(&pass_indirectly_span) =
find_attr!(attrs, AttributeKind::RustcPassIndirectlyInNonRusticAbis(span) => span)
{
self.dcx().emit_err(errors::TransparentIncompatible {
hint_spans: vec![span, pass_indirectly_span],
target: target.to_string(),
});
}
if is_explicit_rust && (int_reprs > 0 || is_c || is_simd) {
let hint_spans = hint_spans.clone().collect();
self.dcx().emit_err(errors::ReprConflicting { hint_spans });

View file

@ -1944,6 +1944,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,

View file

@ -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,

View file

@ -10,11 +10,15 @@ where
ret.extend_integer_width_to(32);
}
fn classify_arg<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>)
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
arg.extend_integer_width_to(32);
}

View file

@ -65,6 +65,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() {
arg.extend_integer_width_to(32);
return;

View file

@ -30,6 +30,8 @@
//! compatible with AVR-GCC - Rust and AVR-GCC only differ in the small amount
//! of compiler frontend specific calling convention logic implemented here.
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi};
fn classify_ret_ty<Ty>(ret: &mut ArgAbi<'_, Ty>) {
@ -38,13 +40,23 @@ fn classify_ret_ty<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg_ty<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg_ty<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if arg.layout.is_aggregate() {
arg.make_indirect();
}
}
pub(crate) fn compute_abi_info<Ty>(fty: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if !fty.ret.is_ignore() {
classify_ret_ty(&mut fty.ret);
}
@ -54,6 +66,6 @@ pub(crate) fn compute_abi_info<Ty>(fty: &mut FnAbi<'_, Ty>) {
continue;
}
classify_arg_ty(arg);
classify_arg_ty(cx, arg);
}
}

View file

@ -1,4 +1,6 @@
// see https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/BPF/BPFCallingConv.td
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi};
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
@ -9,7 +11,14 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if arg.layout.is_aggregate() || arg.layout.size.bits() > 64 {
arg.make_indirect();
} else {
@ -17,7 +26,10 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
}
}
pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(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);
}
@ -26,7 +38,7 @@ pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
if arg.is_ignore() {
continue;
}
classify_arg(arg);
classify_arg(cx, arg);
}
}

View file

@ -4,6 +4,8 @@
// Reference: Clang CSKY lowering code
// https://github.com/llvm/llvm-project/blob/4a074f32a6914f2a8d7215d78758c24942dddc3d/clang/lib/CodeGen/Targets/CSKY.cpp#L76-L162
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform};
fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) {
@ -27,11 +29,18 @@ fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if !arg.layout.is_sized() {
// Not touching this...
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
// For argument type, the first 4*XLen parts of aggregate will be passed
// in registers, and the rest will be passed in stack.
// So we can coerce to integers directly and let backend handle it correctly.
@ -47,7 +56,10 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
}
}
pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(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);
}
@ -56,6 +68,6 @@ pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
if arg.is_ignore() {
continue;
}
classify_arg(arg);
classify_arg(cx, arg);
}
}

View file

@ -1,3 +1,5 @@
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi};
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
@ -8,7 +10,14 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 {
arg.make_indirect();
} else {
@ -16,7 +25,10 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
}
}
pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(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);
}
@ -25,6 +37,6 @@ pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
if arg.is_ignore() {
continue;
}
classify_arg(arg);
classify_arg(cx, arg);
}
}

View file

@ -287,6 +287,11 @@ fn classify_arg<'a, Ty, C>(
// Not touching this...
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
*avail_gprs = (*avail_gprs).saturating_sub(1);
return;
}
if !is_vararg {
match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {

View file

@ -1,3 +1,5 @@
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi};
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
@ -8,11 +10,18 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if !arg.layout.is_sized() {
// Not touching this...
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if arg.layout.is_aggregate() {
arg.pass_by_stack_offset(None);
} else {
@ -20,7 +29,10 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
}
}
pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(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);
}
@ -29,6 +41,6 @@ pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
if arg.is_ignore() {
continue;
}
classify_arg(arg);
classify_arg(cx, arg);
}
}

View file

@ -1,4 +1,4 @@
use rustc_abi::{HasDataLayout, Size};
use rustc_abi::{HasDataLayout, Size, TyAbiInterface};
use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform};
@ -14,18 +14,26 @@ where
}
}
fn classify_arg<Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, offset: &mut Size)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !arg.layout.is_sized() {
// FIXME: Update offset?
// Not touching this...
return;
}
let dl = cx.data_layout();
let size = arg.layout.size;
let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align);
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
*offset = offset.align_to(align) + dl.pointer_size().align_to(align);
return;
}
let size = arg.layout.size;
if arg.layout.is_aggregate() {
let pad_i32 = !offset.is_aligned(align);
arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32);
@ -36,8 +44,9 @@ where
*offset = offset.align_to(align) + size.align_to(align);
}
pub(crate) fn compute_abi_info<Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, 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,
{
let mut offset = Size::ZERO;

View file

@ -82,6 +82,10 @@ where
extend_integer_width_mips(arg, 64);
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
let dl = cx.data_layout();
let size = arg.layout.size;

View file

@ -677,30 +677,30 @@ impl<'a, Ty> FnAbi<'a, Ty> {
}
"amdgpu" => amdgpu::compute_abi_info(cx, self),
"arm" => arm::compute_abi_info(cx, self),
"avr" => avr::compute_abi_info(self),
"avr" => avr::compute_abi_info(cx, self),
"loongarch32" | "loongarch64" => loongarch::compute_abi_info(cx, self),
"m68k" => m68k::compute_abi_info(self),
"csky" => csky::compute_abi_info(self),
"m68k" => m68k::compute_abi_info(cx, self),
"csky" => csky::compute_abi_info(cx, self),
"mips" | "mips32r6" => mips::compute_abi_info(cx, self),
"mips64" | "mips64r6" => mips64::compute_abi_info(cx, self),
"powerpc" => powerpc::compute_abi_info(cx, self),
"powerpc64" => powerpc64::compute_abi_info(cx, self),
"s390x" => s390x::compute_abi_info(cx, self),
"msp430" => msp430::compute_abi_info(self),
"msp430" => msp430::compute_abi_info(cx, self),
"sparc" => sparc::compute_abi_info(cx, self),
"sparc64" => sparc64::compute_abi_info(cx, self),
"nvptx64" => {
if abi == ExternAbi::PtxKernel || abi == ExternAbi::GpuKernel {
nvptx64::compute_ptx_kernel_abi_info(cx, self)
} else {
nvptx64::compute_abi_info(self)
nvptx64::compute_abi_info(cx, self)
}
}
"hexagon" => hexagon::compute_abi_info(self),
"hexagon" => hexagon::compute_abi_info(cx, self),
"xtensa" => xtensa::compute_abi_info(cx, self),
"riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
"wasm32" | "wasm64" => wasm::compute_abi_info(cx, self),
"bpf" => bpf::compute_abi_info(self),
"bpf" => bpf::compute_abi_info(cx, self),
arch => panic!("no lowering implemented for {arch}"),
}
}

View file

@ -1,6 +1,8 @@
// Reference: MSP430 Embedded Application Binary Interface
// https://www.ti.com/lit/an/slaa534a/slaa534a.pdf
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi};
// 3.5 Structures or Unions Passed and Returned by Reference
@ -17,7 +19,14 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 {
arg.make_indirect();
} else {
@ -25,7 +34,10 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
}
}
pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(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);
}
@ -34,6 +46,6 @@ pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
if arg.is_ignore() {
continue;
}
classify_arg(arg);
classify_arg(cx, arg);
}
}

View file

@ -11,7 +11,14 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if arg.layout.is_aggregate() && arg.layout.is_sized() {
classify_aggregate(arg)
} else if arg.layout.size.bits() < 32 && arg.layout.is_sized() {
@ -81,7 +88,10 @@ where
}
}
pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C>(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);
}
@ -90,7 +100,7 @@ pub(crate) fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
if arg.is_ignore() {
continue;
}
classify_arg(arg);
classify_arg(cx, arg);
}
}

View file

@ -1,3 +1,5 @@
use rustc_abi::TyAbiInterface;
use crate::callconv::{ArgAbi, FnAbi};
use crate::spec::HasTargetSpec;
@ -9,7 +11,10 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}
fn classify_arg<Ty>(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<Ty>(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<Ty>(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);
}

View file

@ -52,6 +52,10 @@ where
// Not touching this...
return;
}
if !is_ret && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if !arg.layout.is_aggregate() {
arg.extend_integer_width_to(64);
return;

View file

@ -290,9 +290,15 @@ fn classify_arg<'a, Ty, C>(
Ty: TyAbiInterface<'a, C> + Copy,
{
if !arg.layout.is_sized() {
// FIXME: Update avail_gprs?
// Not touching this...
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
*avail_gprs = (*avail_gprs).saturating_sub(1);
return;
}
if !is_vararg {
match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {

View file

@ -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 {

View file

@ -1,4 +1,4 @@
use rustc_abi::{HasDataLayout, Size};
use rustc_abi::{HasDataLayout, Size, TyAbiInterface};
use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform};
@ -14,15 +14,22 @@ where
}
}
fn classify_arg<Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, offset: &mut Size)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !arg.layout.is_sized() {
// FIXME: Update offset?
// Not touching this...
return;
}
let dl = cx.data_layout();
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
*offset += dl.pointer_size();
return;
}
let size = arg.layout.size;
let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align);
@ -36,8 +43,9 @@ where
*offset = offset.align_to(align) + size.align_to(align);
}
pub(crate) fn compute_abi_info<Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, 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,
{
let mut offset = Size::ZERO;

View file

@ -140,6 +140,10 @@ where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if !arg.layout.is_aggregate() {
arg.extend_integer_width_to(64);
return;

View file

@ -52,6 +52,10 @@ where
// Not touching this...
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
arg.extend_integer_width_to(32);
if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) {
arg.make_indirect();

View file

@ -64,6 +64,11 @@ where
continue;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
continue;
}
let t = cx.target_spec();
let align_4 = Align::from_bytes(4).unwrap();
let align_16 = Align::from_bytes(16).unwrap();

View file

@ -183,9 +183,15 @@ where
let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| {
if !arg.layout.is_sized() {
// FIXME: Update int_regs?
// 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 {

View file

@ -46,6 +46,11 @@ pub(crate) fn compute_abi_info<'a, Ty, C>(
continue;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
continue;
}
// FIXME: MSVC 2015+ will pass the first 3 vector arguments in [XYZ]MM0-2
// See https://reviews.llvm.org/D72114 for Clang behavior

View file

@ -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<Ty>(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<Ty>(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.

View file

@ -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);
}
}

View file

@ -1,3 +1,4 @@
use std::assert_matches::assert_matches;
use std::iter;
use rustc_abi::Primitive::Pointer;
@ -388,6 +389,12 @@ fn fn_abi_sanity_check<'tcx>(
if let PassMode::Indirect { on_stack, .. } = arg.mode {
assert!(!on_stack, "rust abi shouldn't use on_stack");
}
} else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
assert_matches!(
arg.mode,
PassMode::Indirect { on_stack: false, .. },
"the {spec_abi} ABI does not implement `#[rustc_pass_indirectly_in_non_rustic_abis]`"
);
}
match &arg.mode {

View file

@ -639,8 +639,8 @@ fn layout_of_uncached<'tcx>(
// UnsafeCell and UnsafePinned both disable niche optimizations
let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned();
let get_discriminant_type =
|min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max);
let discr_range_of_repr =
|min, max| abi::Integer::discr_range_of_repr(tcx, ty, &def.repr(), min, max);
let discriminants_iter = || {
def.is_enum()
@ -663,7 +663,7 @@ fn layout_of_uncached<'tcx>(
def.is_enum(),
is_special_no_niche,
tcx.layout_scalar_valid_range(def.did()),
get_discriminant_type,
discr_range_of_repr,
discriminants_iter(),
!maybe_unsized,
)
@ -688,7 +688,7 @@ fn layout_of_uncached<'tcx>(
def.is_enum(),
is_special_no_niche,
tcx.layout_scalar_valid_range(def.did()),
get_discriminant_type,
discr_range_of_repr,
discriminants_iter(),
!maybe_unsized,
) else {

View file

@ -14,6 +14,8 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
if layout.size.bytes() >= tcx.data_layout.obj_size_bound() {
bug!("size is too large, in the following layout:\n{layout:#?}");
}
// FIXME(#124403): Once `repr_c_enums_larger_than_int` is a hard error, we could assert
// here that a repr(c) enum discriminant is never larger than a c_int.
if !cfg!(debug_assertions) {
// Stop here, the rest is kind of expensive.

View file

@ -299,3 +299,15 @@ impl<'f> Drop for VaListImpl<'f> {
// This works for now, since `va_end` is a no-op on all current LLVM targets.
}
}
// Checks (via an assert in `compiler/rustc_ty_utils/src/abi.rs`) that the C ABI for the current
// target correctly implements `rustc_pass_indirectly_in_non_rustic_abis`.
const _: () = {
#[repr(C)]
#[rustc_pass_indirectly_in_non_rustic_abis]
struct Type(usize);
const extern "C" fn c(_: Type) {}
c(Type(0))
};

View file

@ -3337,6 +3337,42 @@ fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) {
builder.remove_dir(dir);
}
/// Runs unit tests in `bootstrap_test.py`, which test the Python parts of bootstrap.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct BootstrapPy;
impl Step for BootstrapPy {
type Output = ();
const DEFAULT: bool = true;
const IS_HOST: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
// Bootstrap tests might not be perfectly self-contained and can depend
// on the environment, so only run them by default in CI, not locally.
// See `test::Bootstrap::should_run`.
let is_ci = run.builder.config.is_running_on_ci;
run.alias("bootstrap-py").default_condition(is_ci)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(BootstrapPy)
}
fn run(self, builder: &Builder<'_>) -> Self::Output {
let mut check_bootstrap = command(builder.python());
check_bootstrap
.args(["-m", "unittest", "bootstrap_test.py"])
// Forward command-line args after `--` to unittest, for filtering etc.
.args(builder.config.test_args())
.env("BUILD_DIR", &builder.out)
.env("BUILD_PLATFORM", builder.build.host_target.triple)
.env("BOOTSTRAP_TEST_RUSTC_BIN", &builder.initial_rustc)
.env("BOOTSTRAP_TEST_CARGO_BIN", &builder.initial_cargo)
.current_dir(builder.src.join("src/bootstrap/"));
check_bootstrap.delay_failure().run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Bootstrap;
@ -3353,18 +3389,6 @@ impl Step for Bootstrap {
// Some tests require cargo submodule to be present.
builder.build.require_submodule("src/tools/cargo", None);
let mut check_bootstrap = command(builder.python());
check_bootstrap
.args(["-m", "unittest", "bootstrap_test.py"])
.env("BUILD_DIR", &builder.out)
.env("BUILD_PLATFORM", builder.build.host_target.triple)
.env("BOOTSTRAP_TEST_RUSTC_BIN", &builder.initial_rustc)
.env("BOOTSTRAP_TEST_CARGO_BIN", &builder.initial_cargo)
.current_dir(builder.src.join("src/bootstrap/"));
// NOTE: we intentionally don't pass test_args here because the args for unittest and cargo test are mutually incompatible.
// Use `python -m unittest` manually if you want to pass arguments.
check_bootstrap.delay_failure().run(builder);
let mut cargo = tool::prepare_tool_cargo(
builder,
build_compiler,

View file

@ -869,6 +869,7 @@ impl<'a> Builder<'a> {
Kind::Test => describe!(
crate::core::build_steps::toolstate::ToolStateCheck,
test::Tidy,
test::BootstrapPy,
test::Bootstrap,
test::Ui,
test::Crashes,

View file

@ -177,6 +177,21 @@ impl Add<isize> for isize {
}
}
#[lang = "neg"]
pub trait Neg {
type Output;
fn neg(self) -> Self::Output;
}
impl Neg for isize {
type Output = isize;
fn neg(self) -> isize {
loop {} // Dummy impl, not actually used
}
}
#[lang = "sync"]
trait Sync {}
impl_marker_trait!(
@ -231,6 +246,13 @@ pub mod mem {
#[rustc_nounwind]
#[rustc_intrinsic]
pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst;
#[rustc_nounwind]
#[rustc_intrinsic]
pub const fn size_of<T>() -> usize;
#[rustc_nounwind]
#[rustc_intrinsic]
pub const fn align_of<T>() -> usize;
}
#[lang = "c_void"]

View file

@ -0,0 +1,38 @@
//@ add-minicore
//@ check-fail
//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED"
//@ ignore-backends: gcc
#![feature(rustc_attrs)]
#![crate_type = "lib"]
#![feature(no_core)]
#![no_std]
#![no_core]
extern crate minicore;
use minicore::*;
#[repr(C)]
#[rustc_pass_indirectly_in_non_rustic_abis]
pub struct Type(u8);
#[rustc_abi(debug)]
pub extern "C" fn extern_c(_: Type) {}
//~^ ERROR fn_abi_of(extern_c) = FnAbi {
//~| ERROR mode: Indirect
//~| ERROR on_stack: false,
//~| ERROR conv: C,
#[rustc_abi(debug)]
pub extern "Rust" fn extern_rust(_: Type) {}
//~^ ERROR fn_abi_of(extern_rust) = FnAbi {
//~| ERROR mode: Cast
//~| ERROR conv: Rust
#[repr(transparent)]
struct Inner(u64);
#[rustc_pass_indirectly_in_non_rustic_abis]
//~^ ERROR transparent struct cannot have other repr hints
#[repr(transparent)]
struct Wrapper(Inner);

View file

@ -0,0 +1,194 @@
error[E0692]: transparent struct cannot have other repr hints
--> $DIR/pass-indirectly-attr.rs:35:1
|
LL | #[rustc_pass_indirectly_in_non_rustic_abis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | struct Wrapper(Inner);
| ^^^^^^^^^^^^^^^^^^^^^^
error: fn_abi_of(extern_c) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: Type,
layout: Layout {
size: Size(1 bytes),
align: AbiAlign {
abi: Align(1 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(1 bytes),
randomization_seed: $SEED,
},
},
mode: Indirect {
attrs: ArgAttributes {
regular: CapturesAddress | NoAlias | NonNull | NoUndef,
arg_ext: None,
pointee_size: Size(1 bytes),
pointee_align: Some(
Align(1 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:20:1
|
LL | pub extern "C" fn extern_c(_: Type) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: fn_abi_of(extern_rust) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: Type,
layout: Layout {
size: Size(1 bytes),
align: AbiAlign {
abi: Align(1 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(1 bytes),
randomization_seed: $SEED,
},
},
mode: Cast {
pad_i32: false,
cast: CastTarget {
prefix: [
None,
None,
None,
None,
None,
None,
None,
None,
],
rest_offset: None,
rest: Uniform {
unit: Reg {
kind: Integer,
size: Size(1 bytes),
},
total: Size(1 bytes),
is_consecutive: false,
},
attrs: ArgAttributes {
regular: ,
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: Rust,
can_unwind: false,
}
--> $DIR/pass-indirectly-attr.rs:27:1
|
LL | pub extern "Rust" fn extern_rust(_: Type) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0692`.

View file

@ -0,0 +1,15 @@
//@ check-fail
#![feature(rustc_attrs)]
#![crate_type = "lib"]
#[rustc_pass_indirectly_in_non_rustic_abis]
//~^ ERROR: `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute cannot be used on functions
fn not_a_struct() {}
#[repr(C)]
#[rustc_pass_indirectly_in_non_rustic_abis]
struct YesAStruct {
foo: u8,
bar: u16,
}

View file

@ -0,0 +1,10 @@
error: `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute cannot be used on functions
--> $DIR/pass-indirectly.rs:6:1
|
LL | #[rustc_pass_indirectly_in_non_rustic_abis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to structs
error: aborting due to 1 previous error

View file

@ -2,6 +2,7 @@
#![feature(core_intrinsics)]
use std::intrinsics::discriminant_value;
use std::mem::size_of;
enum E1 {
A,
@ -20,6 +21,14 @@ enum E3 {
B = 100,
}
// Enums like this are found in the ecosystem, let's make sure they get the right size.
#[repr(C)]
#[allow(overflowing_literals)]
enum UnsignedIntEnum {
A = 0,
O = 0xffffffff, // doesn't fit into `int`, but fits into `unsigned int`
}
#[repr(i128)]
enum E4 {
A = 0x1223_3445_5667_7889,
@ -27,24 +36,38 @@ enum E4 {
}
fn main() {
assert_eq!(size_of::<E1>(), 1);
let mut target: [isize; 3] = [0, 0, 0];
target[1] = discriminant_value(&E1::A);
assert_eq!(target, [0, 0, 0]);
target[1] = discriminant_value(&E1::B);
assert_eq!(target, [0, 1, 0]);
assert_eq!(size_of::<E2>(), 1);
let mut target: [i8; 3] = [0, 0, 0];
target[1] = discriminant_value(&E2::A);
assert_eq!(target, [0, 7, 0]);
target[1] = discriminant_value(&E2::B);
assert_eq!(target, [0, -2, 0]);
// E3's size is target-dependent
let mut target: [isize; 3] = [0, 0, 0];
target[1] = discriminant_value(&E3::A);
assert_eq!(target, [0, 42, 0]);
target[1] = discriminant_value(&E3::B);
assert_eq!(target, [0, 100, 0]);
#[allow(overflowing_literals)]
{
assert_eq!(size_of::<UnsignedIntEnum>(), 4);
let mut target: [isize; 3] = [0, -1, 0];
target[1] = discriminant_value(&UnsignedIntEnum::A);
assert_eq!(target, [0, 0, 0]);
target[1] = discriminant_value(&UnsignedIntEnum::O);
assert_eq!(target, [0, 0xffffffff as isize, 0]);
}
assert_eq!(size_of::<E4>(), 16);
let mut target: [i128; 3] = [0, 0, 0];
target[1] = discriminant_value(&E4::A);
assert_eq!(target, [0, 0x1223_3445_5667_7889, 0]);

View file

@ -0,0 +1,35 @@
error: literal out of range for `isize`
--> $DIR/repr-c-big-discriminant1.rs:18:9
|
LL | A = 9223372036854775807, // i64::MAX
| ^^^^^^^^^^^^^^^^^^^
|
= note: the literal `9223372036854775807` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
= note: `#[deny(overflowing_literals)]` on by default
error: literal out of range for `isize`
--> $DIR/repr-c-big-discriminant1.rs:26:9
|
LL | A = -2147483649, // i32::MIN-1
| ^^^^^^^^^^^
|
= note: the literal `-2147483649` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
error: literal out of range for `isize`
--> $DIR/repr-c-big-discriminant1.rs:34:9
|
LL | A = 2147483648, // i32::MAX+1
| ^^^^^^^^^^
|
= note: the literal `2147483648` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
error: literal out of range for `isize`
--> $DIR/repr-c-big-discriminant1.rs:43:9
|
LL | A = 2147483648, // i32::MAX+1
| ^^^^^^^^^^
|
= note: the literal `2147483648` does not fit into the type `isize` whose range is `-2147483648..=2147483647`
error: aborting due to 4 previous errors

View file

@ -0,0 +1,62 @@
error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
--> $DIR/repr-c-big-discriminant1.rs:18:5
|
LL | A = 9223372036854775807, // i64::MAX
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
note: the lint level is defined here
--> $DIR/repr-c-big-discriminant1.rs:8:9
|
LL | #![deny(repr_c_enums_larger_than_int)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
--> $DIR/repr-c-big-discriminant1.rs:26:5
|
LL | A = -2147483649, // i32::MIN-1
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
error: `repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`
--> $DIR/repr-c-big-discriminant1.rs:36:5
|
LL | B = -1,
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`
--> $DIR/repr-c-big-discriminant1.rs:43:5
|
LL | A = 2147483648, // i32::MAX+1
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`
--> $DIR/repr-c-big-discriminant1.rs:53:5
|
LL | A = I64_MAX as isize,
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
error: aborting due to 5 previous errors

View file

@ -0,0 +1,67 @@
//@ revisions: ptr32 ptr64
//@[ptr32] compile-flags: --target i686-unknown-linux-gnu
//@[ptr32] needs-llvm-components: x86
//@[ptr64] compile-flags: --target x86_64-unknown-linux-gnu
//@[ptr64] needs-llvm-components: x86
// GCC doesn't like cross-compilation
//@ ignore-backends: gcc
#![deny(repr_c_enums_larger_than_int)]
//@ add-minicore
#![feature(no_core)]
#![no_core]
extern crate minicore;
use minicore::*;
#[repr(C)]
enum OverflowingEnum1 {
A = 9223372036854775807, // i64::MAX
//[ptr32]~^ ERROR: literal out of range
//[ptr64]~^^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int`
//[ptr64]~^^^ WARN: previously accepted
}
#[repr(C)]
enum OverflowingEnum2 {
A = -2147483649, // i32::MIN-1
//[ptr32]~^ ERROR: literal out of range
//[ptr64]~^^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int`
//[ptr64]~^^^ WARN: previously accepted
}
#[repr(C)]
enum OverflowingEnum3a {
A = 2147483648, // i32::MAX+1
//[ptr32]~^ ERROR: literal out of range
B = -1,
//[ptr64]~^ ERROR: discriminant does not fit into C `unsigned int`, and a previous
//[ptr64]~^^ WARN: previously accepted
}
#[repr(C)]
enum OverflowingEnum3b {
B = -1,
A = 2147483648, // i32::MAX+1
//[ptr32]~^ ERROR: literal out of range
//[ptr64]~^^ ERROR: discriminant does not fit into C `int`, and a previous
//[ptr64]~^^^ WARN: previously accepted
}
const I64_MAX: i64 = 9223372036854775807;
#[repr(C)]
enum OverflowingEnum4 {
A = I64_MAX as isize,
//[ptr64]~^ ERROR: discriminant does not fit into C `int` nor into C `unsigned int`
//[ptr64]~^^ WARN: previously accepted
// No warning/error on 32bit targets, but the `as isize` hints that wrapping is occurring.
}
// Enums like this are found in the ecosystem, let's make sure they get accepted.
#[repr(C)]
#[allow(overflowing_literals)]
enum OkayEnum {
A = 0,
O = 0xffffffff,
}
fn main() {}

View file

@ -0,0 +1,11 @@
error[E0370]: enum discriminant overflowed
--> $DIR/repr-c-big-discriminant2.rs:24:5
|
LL | B, // +1
| ^ overflowed on value after 2147483647
|
= note: explicitly set `B = -2147483648` if that is desired outcome
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0370`.

View file

@ -0,0 +1,18 @@
error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`
--> $DIR/repr-c-big-discriminant2.rs:24:5
|
LL | B, // +1
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124403 <https://github.com/rust-lang/rust/issues/124403>
= note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C
= help: use `repr($int_ty)` instead to explicitly set the size of this enum
note: the lint level is defined here
--> $DIR/repr-c-big-discriminant2.rs:8:9
|
LL | #![deny(repr_c_enums_larger_than_int)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,30 @@
//@ revisions: ptr32 ptr64
//@[ptr32] compile-flags: --target i686-unknown-linux-gnu
//@[ptr32] needs-llvm-components: x86
//@[ptr64] compile-flags: --target x86_64-unknown-linux-gnu
//@[ptr64] needs-llvm-components: x86
// GCC doesn't like cross-compilation
//@ ignore-backends: gcc
#![deny(repr_c_enums_larger_than_int)]
//@ add-minicore
#![feature(no_core)]
#![no_core]
extern crate minicore;
use minicore::*;
// Separate test since it suppresses other errors on ptr32:
// ensure we find the bad discriminant when it is implicitly computed by incrementing
// the previous discriminant.
#[repr(C)]
enum OverflowingEnum {
NEG = -1,
A = 2147483647, // i32::MAX
B, // +1
//[ptr32]~^ ERROR: enum discriminant overflowed
//[ptr64]~^^ ERROR: discriminant does not fit into C `int`
//[ptr64]~^^^ WARN: previously accepted
}
fn main() {}