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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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)>
Expand Down
20 changes: 14 additions & 6 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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))
}
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 4 additions & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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>>,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
70 changes: 54 additions & 16 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_infer/src/infer/snapshot/fudge.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt::Debug;
use std::ops::Range;

use rustc_data_structures::{snapshot_vec as sv, unify as ut};
Expand Down Expand Up @@ -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(|_| {
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_lint/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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! {
Expand Down
50 changes: 50 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
};
}
15 changes: 12 additions & 3 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down
Loading
Loading