Skip to content

Commit 54e27f5

Browse files
Auto merge of #147589 - RalfJung:repr-transparent-uninhabited, r=<try>
[crater only] complain about uninhabited types being ignored in repr(transparent)
2 parents b3f8586 + 0f4ab71 commit 54e27f5

File tree

9 files changed

+212
-51
lines changed

9 files changed

+212
-51
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,8 +1510,26 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15101510
return;
15111511
}
15121512

1513-
// For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
1514-
// "known" respecting #[non_exhaustive] attributes.
1513+
// For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1514+
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1515+
// fields or `repr(C)` or uninhabited. We call those fields "unsuited".
1516+
struct FieldInfo<'tcx> {
1517+
span: Span,
1518+
trivial: bool,
1519+
unsuited: Option<UnsuitedInfo<'tcx>>,
1520+
}
1521+
struct UnsuitedInfo<'tcx> {
1522+
/// The source of the problem, a type that is found somewhere within the field type.
1523+
ty: Ty<'tcx>,
1524+
reason: UnsuitedReason,
1525+
}
1526+
enum UnsuitedReason {
1527+
NonExhaustive,
1528+
PrivateField,
1529+
ReprC,
1530+
Uninhabited,
1531+
}
1532+
15151533
let field_infos = adt.all_fields().map(|field| {
15161534
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
15171535
let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did);
@@ -1520,17 +1538,28 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15201538
let span = tcx.hir_span_if_local(field.did).unwrap();
15211539
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
15221540
if !trivial {
1523-
return (span, trivial, None);
1541+
// No need to even compute `unsuited`.
1542+
return FieldInfo { span, trivial, unsuited: None };
1543+
}
1544+
if layout.unwrap().is_uninhabited() {
1545+
// Uninhabited types aren't really "trivial"...
1546+
// See <https://github.com/rust-lang/rust/issues/135802> for some of the trouble
1547+
// this case used to cause.
1548+
return FieldInfo {
1549+
span,
1550+
trivial,
1551+
unsuited: Some(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }),
1552+
};
15241553
}
1525-
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
15261554

1527-
fn check_non_exhaustive<'tcx>(
1555+
fn check_unsuited<'tcx>(
15281556
tcx: TyCtxt<'tcx>,
1529-
t: Ty<'tcx>,
1530-
) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> {
1531-
match t.kind() {
1532-
ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
1533-
ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
1557+
adt: DefId,
1558+
ty: Ty<'tcx>,
1559+
) -> ControlFlow<UnsuitedInfo<'tcx>> {
1560+
match ty.kind() {
1561+
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, adt, t)),
1562+
ty::Array(ty, _) => check_unsuited(tcx, adt, *ty),
15341563
ty::Adt(def, args) => {
15351564
if !def.did().is_local()
15361565
&& !find_attr!(
@@ -1545,28 +1574,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15451574
.any(ty::VariantDef::is_field_list_non_exhaustive);
15461575
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
15471576
if non_exhaustive || has_priv {
1548-
return ControlFlow::Break((
1549-
def.descr(),
1550-
def.did(),
1551-
args,
1552-
non_exhaustive,
1553-
));
1577+
return ControlFlow::Break(UnsuitedInfo {
1578+
ty,
1579+
reason: if non_exhaustive {
1580+
UnsuitedReason::NonExhaustive
1581+
} else {
1582+
UnsuitedReason::PrivateField
1583+
},
1584+
});
15541585
}
15551586
}
1587+
if def.repr().c() {
1588+
return ControlFlow::Break(UnsuitedInfo {
1589+
ty,
1590+
reason: UnsuitedReason::ReprC,
1591+
});
1592+
}
15561593
def.all_fields()
15571594
.map(|field| field.ty(tcx, args))
1558-
.try_for_each(|t| check_non_exhaustive(tcx, t))
1595+
.try_for_each(|t| check_unsuited(tcx, adt, t))
15591596
}
15601597
_ => ControlFlow::Continue(()),
15611598
}
15621599
}
15631600

1564-
(span, trivial, check_non_exhaustive(tcx, ty).break_value())
1601+
FieldInfo { span, trivial, unsuited: check_unsuited(tcx, adt.did(), ty).break_value() }
15651602
});
15661603

15671604
let non_trivial_fields = field_infos
15681605
.clone()
1569-
.filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
1606+
.filter_map(|field| if !field.trivial { Some(field.span) } else { None });
15701607
let non_trivial_count = non_trivial_fields.clone().count();
15711608
if non_trivial_count >= 2 {
15721609
bad_non_zero_sized_fields(
@@ -1578,36 +1615,39 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15781615
);
15791616
return;
15801617
}
1581-
let mut prev_non_exhaustive_1zst = false;
1582-
for (span, _trivial, non_exhaustive_1zst) in field_infos {
1583-
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst {
1618+
1619+
let mut prev_unsuited_1zst = false;
1620+
for field in field_infos {
1621+
if let Some(unsuited) = field.unsuited {
1622+
assert!(field.trivial);
15841623
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15851624
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1586-
if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1625+
if non_trivial_count > 0 || prev_unsuited_1zst {
15871626
tcx.node_span_lint(
15881627
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
15891628
tcx.local_def_id_to_hir_id(adt.did().expect_local()),
1590-
span,
1629+
field.span,
15911630
|lint| {
15921631
lint.primary_message(
15931632
"zero-sized fields in `repr(transparent)` cannot \
15941633
contain external non-exhaustive types",
15951634
);
1596-
let note = if non_exhaustive {
1597-
"is marked with `#[non_exhaustive]`"
1598-
} else {
1599-
"contains private fields"
1635+
let note = match unsuited.reason {
1636+
UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`",
1637+
UnsuitedReason::PrivateField => "contains private fields",
1638+
UnsuitedReason::ReprC => "is marked with `#[repr(C)]`",
1639+
UnsuitedReason::Uninhabited => "is not inhabited",
16001640
};
1601-
let field_ty = tcx.def_path_str_with_args(def_id, args);
16021641
lint.note(format!(
1603-
"this {descr} contains `{field_ty}`, which {note}, \
1642+
"this field contains `{field_ty}`, which {note}, \
16041643
and makes it not a breaking change to become \
1605-
non-zero-sized in the future."
1644+
non-zero-sized in the future.",
1645+
field_ty = unsuited.ty,
16061646
));
16071647
},
1608-
)
1648+
);
16091649
} else {
1610-
prev_non_exhaustive_1zst = true;
1650+
prev_unsuited_1zst = true;
16111651
}
16121652
}
16131653
}

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ pub enum FutureIncompatibilityReason {
403403
///
404404
/// After a lint has been in this state for a while and you feel like it is ready to graduate
405405
/// to warning everyone, consider setting [`FutureIncompatibleInfo::report_in_deps`] to true.
406-
/// (see it's documentation for more guidance)
406+
/// (see its documentation for more guidance)
407407
///
408408
/// After some period of time, lints with this variant can be turned into
409409
/// hard errors (and the lint removed). Preferably when there is some

src/build_helper/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub const LLVM_PGO_CRATES: &[&str] = &[
1818
"ripgrep-14.1.1",
1919
"regex-automata-0.4.8",
2020
"clap_derive-4.5.32",
21-
"hyper-1.6.0",
2221
];
2322

2423
/// The default set of crates for opt-dist to collect rustc profiles.

0 commit comments

Comments
 (0)