Skip to content

Commit e5efc33

Browse files
committed
Auto merge of #148472 - Zalathar:rollup-zwlz09o, r=Zalathar
Rollup of 4 pull requests Successful merges: - #144529 (Add `#[rustc_pass_indirectly_in_non_rustic_abis]`) - #147017 (FCW for repr(C) enums whose discriminant values do not fit into a c_int or c_uint) - #148459 (bootstrap: Split out a separate `./x test bootstrap-py` step) - #148468 (add logging to `fudge_inference_if_ok`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 90b6588 + 4f3816b commit e5efc33

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1024
-94
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
812812
let (max, min) = largest_niche
813813
// We might have no inhabited variants, so pretend there's at least one.
814814
.unwrap_or((0, 0));
815-
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
815+
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::discr_range_of_repr(tcx, ty, &repr, min, max);
816816

817817
let mut align = dl.aggregate_align;
818818
let mut max_repr_align = repr.align;

compiler/rustc_abi/src/layout/ty.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
172172
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
173173
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
174174
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
175+
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
176+
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool;
175177
}
176178

177179
impl<'a, Ty> TyAndLayout<'a, Ty> {
@@ -269,6 +271,30 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
269271
Ty::is_transparent(self)
270272
}
271273

274+
/// If this method returns `true`, then this type should always have a `PassMode` of
275+
/// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a
276+
/// non-Rustic ABI (this is true for structs annotated with the
277+
/// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute).
278+
///
279+
/// This is used to replicate some of the behaviour of C array-to-pointer decay; however unlike
280+
/// C any changes the caller makes to the passed value will not be reflected in the callee, so
281+
/// the attribute is only useful for types where observing the value in the caller after the
282+
/// function call isn't allowed (a.k.a. `va_list`).
283+
///
284+
/// This function handles transparent types automatically.
285+
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
286+
where
287+
Ty: TyAbiInterface<'a, C> + Copy,
288+
{
289+
while self.is_transparent()
290+
&& let Some((_, field)) = self.non_1zst_field(cx)
291+
{
292+
self = field;
293+
}
294+
295+
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
296+
}
297+
272298
/// Finds the one field that is not a 1-ZST.
273299
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
274300
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>

compiler/rustc_abi/src/lib.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,17 @@ bitflags! {
8888
const IS_C = 1 << 0;
8989
const IS_SIMD = 1 << 1;
9090
const IS_TRANSPARENT = 1 << 2;
91-
// Internal only for now. If true, don't reorder fields.
92-
// On its own it does not prevent ABI optimizations.
91+
/// Internal only for now. If true, don't reorder fields.
92+
/// On its own it does not prevent ABI optimizations.
9393
const IS_LINEAR = 1 << 3;
94-
// If true, the type's crate has opted into layout randomization.
95-
// Other flags can still inhibit reordering and thus randomization.
96-
// The seed stored in `ReprOptions.field_shuffle_seed`.
94+
/// If true, the type's crate has opted into layout randomization.
95+
/// Other flags can still inhibit reordering and thus randomization.
96+
/// The seed stored in `ReprOptions.field_shuffle_seed`.
9797
const RANDOMIZE_LAYOUT = 1 << 4;
98-
// Any of these flags being set prevent field reordering optimisation.
98+
/// If true, the type is always passed indirectly by non-Rustic ABIs.
99+
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
100+
const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5;
101+
/// Any of these flags being set prevent field reordering optimisation.
99102
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
100103
| ReprFlags::IS_SIMD.bits()
101104
| ReprFlags::IS_LINEAR.bits();
@@ -183,6 +186,11 @@ impl ReprOptions {
183186

184187
/// Returns the discriminant type, given these `repr` options.
185188
/// This must only be called on enums!
189+
///
190+
/// This is the "typeck type" of the discriminant, which is effectively the maximum size:
191+
/// discriminant values will be wrapped to fit (with a lint). Layout can later decide to use a
192+
/// smaller type for the tag that stores the discriminant at runtime and that will work just
193+
/// fine, it just induces casts when getting/setting the discriminant.
186194
pub fn discr_type(&self) -> IntegerType {
187195
self.int.unwrap_or(IntegerType::Pointer(true))
188196
}

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,3 +676,12 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
676676
Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
677677
}
678678
}
679+
680+
pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
681+
682+
impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
683+
const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
684+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
685+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
686+
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
687+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ use crate::attributes::allow_unstable::{
2020
use crate::attributes::body::CoroutineParser;
2121
use crate::attributes::codegen_attrs::{
2222
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
23-
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser,
24-
TargetFeatureParser, TrackCallerParser, UsedParser,
23+
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
24+
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
25+
TrackCallerParser, UsedParser,
2526
};
2627
use crate::attributes::confusables::ConfusablesParser;
2728
use crate::attributes::crate_level::{
@@ -243,6 +244,7 @@ attribute_parsers!(
243244
Single<WithoutArgs<PubTransparentParser>>,
244245
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
245246
Single<WithoutArgs<RustcMainParser>>,
247+
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
246248
Single<WithoutArgs<SpecializationTraitParser>>,
247249
Single<WithoutArgs<StdInternalSymbolParser>>,
248250
Single<WithoutArgs<TrackCallerParser>>,

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
657657
template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-naked-attribute"),
658658
WarnFollowing, EncodeCrossCrate::No
659659
),
660+
// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
661+
rustc_attr!(
662+
rustc_pass_indirectly_in_non_rustic_abis, Normal, template!(Word), ErrorFollowing,
663+
EncodeCrossCrate::No,
664+
"types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic abis."
665+
),
660666

661667
// Limits:
662668
ungated!(

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,9 @@ pub enum AttributeKind {
679679
/// Represents `#[rustc_object_lifetime_default]`.
680680
RustcObjectLifetimeDefault,
681681

682+
/// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
683+
RustcPassIndirectlyInNonRusticAbis(Span),
684+
682685
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
683686
RustcSimdMonomorphizeLaneLimit(Limit),
684687

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ impl AttributeKind {
9191
RustcLayoutScalarValidRangeStart(..) => Yes,
9292
RustcMain => No,
9393
RustcObjectLifetimeDefault => No,
94+
RustcPassIndirectlyInNonRusticAbis(..) => No,
9495
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
9596
Sanitize { .. } => No,
9697
ShouldPanic { .. } => No,

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
782782
tcx.ensure_ok().generics_of(def_id);
783783
tcx.ensure_ok().type_of(def_id);
784784
tcx.ensure_ok().predicates_of(def_id);
785-
crate::collect::lower_enum_variant_types(tcx, def_id.to_def_id());
785+
crate::collect::lower_enum_variant_types(tcx, def_id);
786786
check_enum(tcx, def_id);
787787
check_variances_for_type_defn(tcx, def_id);
788788
}

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::cell::Cell;
1919
use std::iter;
2020
use std::ops::Bound;
2121

22-
use rustc_abi::ExternAbi;
22+
use rustc_abi::{ExternAbi, Size};
2323
use rustc_ast::Recovered;
2424
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
2525
use rustc_data_structures::unord::UnordMap;
@@ -605,32 +605,70 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {
605605
tcx.ensure_ok().predicates_of(def_id);
606606
}
607607

608-
pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
608+
pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) {
609609
let def = tcx.adt_def(def_id);
610610
let repr_type = def.repr().discr_type();
611611
let initial = repr_type.initial_discriminant(tcx);
612612
let mut prev_discr = None::<Discr<'_>>;
613+
// Some of the logic below relies on `i128` being able to hold all c_int and c_uint values.
614+
assert!(tcx.sess.target.c_int_width < 128);
615+
let mut min_discr = i128::MAX;
616+
let mut max_discr = i128::MIN;
613617

614618
// fill the discriminant values and field types
615619
for variant in def.variants() {
616620
let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx));
617-
prev_discr = Some(
618-
if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr {
619-
def.eval_explicit_discr(tcx, const_def_id).ok()
620-
} else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
621-
Some(discr)
622-
} else {
621+
let cur_discr = if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr {
622+
def.eval_explicit_discr(tcx, const_def_id).ok()
623+
} else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
624+
Some(discr)
625+
} else {
626+
let span = tcx.def_span(variant.def_id);
627+
tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed {
628+
span,
629+
discr: prev_discr.unwrap().to_string(),
630+
item_name: tcx.item_ident(variant.def_id),
631+
wrapped_discr: wrapped_discr.to_string(),
632+
});
633+
None
634+
}
635+
.unwrap_or(wrapped_discr);
636+
637+
if def.repr().c() {
638+
let c_int = Size::from_bits(tcx.sess.target.c_int_width);
639+
let c_uint_max = i128::try_from(c_int.unsigned_int_max()).unwrap();
640+
// c_int is a signed type, so get a proper signed version of the discriminant
641+
let discr_size = cur_discr.ty.int_size_and_signed(tcx).0;
642+
let discr_val = discr_size.sign_extend(cur_discr.val);
643+
min_discr = min_discr.min(discr_val);
644+
max_discr = max_discr.max(discr_val);
645+
646+
// The discriminant range must either fit into c_int or c_uint.
647+
if !(min_discr >= c_int.signed_int_min() && max_discr <= c_int.signed_int_max())
648+
&& !(min_discr >= 0 && max_discr <= c_uint_max)
649+
{
623650
let span = tcx.def_span(variant.def_id);
624-
tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed {
651+
let msg = if discr_val < c_int.signed_int_min() || discr_val > c_uint_max {
652+
"`repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`"
653+
} else if discr_val < 0 {
654+
"`repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`"
655+
} else {
656+
"`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`"
657+
};
658+
tcx.node_span_lint(
659+
rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT,
660+
tcx.local_def_id_to_hir_id(def_id),
625661
span,
626-
discr: prev_discr.unwrap().to_string(),
627-
item_name: tcx.item_ident(variant.def_id),
628-
wrapped_discr: wrapped_discr.to_string(),
629-
});
630-
None
662+
|d| {
663+
d.primary_message(msg)
664+
.note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C")
665+
.help("use `repr($int_ty)` instead to explicitly set the size of this enum");
666+
}
667+
);
631668
}
632-
.unwrap_or(wrapped_discr),
633-
);
669+
}
670+
671+
prev_discr = Some(cur_discr);
634672

635673
for f in &variant.fields {
636674
tcx.ensure_ok().generics_of(f.did);

0 commit comments

Comments
 (0)