Skip to content

Commit 0de49ae

Browse files
Auto merge of #147185 - RalfJung:repr-c-not-zst, r=<try>
Repr c not zst
2 parents a2db928 + 24d6a9a commit 0de49ae

File tree

6 files changed

+156
-141
lines changed

6 files changed

+156
-141
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 85 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind};
1212
use rustc_hir::{LangItem, Node, attrs, find_attr, intravisit};
1313
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
1414
use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc};
15-
use rustc_lint_defs::builtin::{
16-
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS,
17-
};
15+
use rustc_lint_defs::builtin::UNSUPPORTED_CALLING_CONVENTIONS;
1816
use rustc_middle::hir::nested_filter;
1917
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
2018
use rustc_middle::middle::stability::EvalResult;
@@ -1510,58 +1508,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15101508
return;
15111509
}
15121510

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.
1511+
// For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
15151512
let field_infos = adt.all_fields().map(|field| {
15161513
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
15171514
let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did);
15181515
let layout = tcx.layout_of(typing_env.as_query_input(ty));
15191516
// We are currently checking the type this field came from, so it must be local
15201517
let span = tcx.hir_span_if_local(field.did).unwrap();
15211518
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
1522-
if !trivial {
1523-
return (span, trivial, None);
1524-
}
1525-
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
1526-
1527-
fn check_non_exhaustive<'tcx>(
1528-
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),
1534-
ty::Adt(def, args) => {
1535-
if !def.did().is_local()
1536-
&& !find_attr!(
1537-
tcx.get_all_attrs(def.did()),
1538-
AttributeKind::PubTransparent(_)
1539-
)
1540-
{
1541-
let non_exhaustive = def.is_variant_list_non_exhaustive()
1542-
|| def
1543-
.variants()
1544-
.iter()
1545-
.any(ty::VariantDef::is_field_list_non_exhaustive);
1546-
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
1547-
if non_exhaustive || has_priv {
1548-
return ControlFlow::Break((
1549-
def.descr(),
1550-
def.did(),
1551-
args,
1552-
non_exhaustive,
1553-
));
1554-
}
1555-
}
1556-
def.all_fields()
1557-
.map(|field| field.ty(tcx, args))
1558-
.try_for_each(|t| check_non_exhaustive(tcx, t))
1559-
}
1560-
_ => ControlFlow::Continue(()),
1561-
}
1562-
}
1563-
1564-
(span, trivial, check_non_exhaustive(tcx, ty).break_value())
1519+
(span, trivial, ty)
15651520
});
15661521

15671522
let non_trivial_fields = field_infos
@@ -1578,36 +1533,94 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15781533
);
15791534
return;
15801535
}
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 {
1536+
1537+
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1538+
// fields or `repr(C)`. We call those fields "unsuited". Search for unsuited fields and
1539+
// error if the repr(transparent) condition relies on them.
1540+
enum UnsuitedReason {
1541+
NonExhaustive,
1542+
PrivateField,
1543+
ReprC,
1544+
}
1545+
struct UnsuitedInfo<'tcx> {
1546+
descr: &'static str,
1547+
def_id: DefId,
1548+
args: GenericArgsRef<'tcx>,
1549+
reason: UnsuitedReason,
1550+
}
1551+
1552+
fn check_unsuited_1zst<'tcx>(
1553+
tcx: TyCtxt<'tcx>,
1554+
t: Ty<'tcx>,
1555+
) -> ControlFlow<UnsuitedInfo<'tcx>> {
1556+
match t.kind() {
1557+
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited_1zst(tcx, t)),
1558+
ty::Array(ty, _) => check_unsuited_1zst(tcx, *ty),
1559+
ty::Adt(def, args) => {
1560+
if !def.did().is_local()
1561+
&& !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_))
1562+
{
1563+
let non_exhaustive = def.is_variant_list_non_exhaustive()
1564+
|| def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive);
1565+
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
1566+
if non_exhaustive || has_priv {
1567+
return ControlFlow::Break(UnsuitedInfo {
1568+
descr: def.descr(),
1569+
def_id: def.did(),
1570+
args,
1571+
reason: if non_exhaustive {
1572+
UnsuitedReason::NonExhaustive
1573+
} else {
1574+
UnsuitedReason::PrivateField
1575+
},
1576+
});
1577+
}
1578+
}
1579+
if def.repr().c() {
1580+
return ControlFlow::Break(UnsuitedInfo {
1581+
descr: def.descr(),
1582+
def_id: def.did(),
1583+
args,
1584+
reason: UnsuitedReason::ReprC,
1585+
});
1586+
}
1587+
def.all_fields()
1588+
.map(|field| field.ty(tcx, args))
1589+
.try_for_each(|t| check_unsuited_1zst(tcx, t))
1590+
}
1591+
_ => ControlFlow::Continue(()),
1592+
}
1593+
}
1594+
1595+
let mut prev_unsuited_1zst = false;
1596+
for (span, trivial, ty) in field_infos {
1597+
if !trivial {
1598+
continue;
1599+
}
1600+
if let Some(unsuited) = check_unsuited_1zst(tcx, ty).break_value() {
15841601
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15851602
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1586-
if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1587-
tcx.node_span_lint(
1588-
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
1589-
tcx.local_def_id_to_hir_id(adt.did().expect_local()),
1603+
if non_trivial_count > 0 || prev_unsuited_1zst {
1604+
let mut diag = tcx.dcx().struct_span_err(
15901605
span,
1591-
|lint| {
1592-
lint.primary_message(
1593-
"zero-sized fields in `repr(transparent)` cannot \
1606+
"zero-sized fields in `repr(transparent)` cannot \
15941607
contain external non-exhaustive types",
1595-
);
1596-
let note = if non_exhaustive {
1597-
"is marked with `#[non_exhaustive]`"
1598-
} else {
1599-
"contains private fields"
1600-
};
1601-
let field_ty = tcx.def_path_str_with_args(def_id, args);
1602-
lint.note(format!(
1603-
"this {descr} contains `{field_ty}`, which {note}, \
1608+
);
1609+
let note = match unsuited.reason {
1610+
UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`",
1611+
UnsuitedReason::PrivateField => "contains private fields",
1612+
UnsuitedReason::ReprC => "is marked with `#[repr(C)]`",
1613+
};
1614+
let field_ty = tcx.def_path_str_with_args(unsuited.def_id, unsuited.args);
1615+
diag.note(format!(
1616+
"this {descr} contains `{field_ty}`, which {note}, \
16041617
and makes it not a breaking change to become \
1605-
non-zero-sized in the future."
1606-
));
1607-
},
1608-
)
1618+
non-zero-sized in the future.",
1619+
descr = unsuited.descr,
1620+
));
1621+
diag.emit();
16091622
} else {
1610-
prev_non_exhaustive_1zst = true;
1623+
prev_unsuited_1zst = true;
16111624
}
16121625
}
16131626
}

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

tests/ui/repr/repr-transparent-non-exhaustive.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,82 +36,66 @@ pub struct T4(Sized, ExternalIndirection<(InternalPrivate, InternalNonExhaustive
3636
#[repr(transparent)]
3737
pub struct T5(Sized, Private);
3838
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
39-
//~| WARN this was previously accepted by the compiler
4039

4140
#[repr(transparent)]
4241
pub struct T6(Sized, NonExhaustive);
4342
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
44-
//~| WARN this was previously accepted by the compiler
4543

4644
#[repr(transparent)]
4745
pub struct T7(Sized, NonExhaustiveEnum);
4846
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
49-
//~| WARN this was previously accepted by the compiler
5047

5148
#[repr(transparent)]
5249
pub struct T8(Sized, NonExhaustiveVariant);
5350
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
54-
//~| WARN this was previously accepted by the compiler
5551

5652
#[repr(transparent)]
5753
pub struct T9(Sized, InternalIndirection<Private>);
5854
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
59-
//~| WARN this was previously accepted by the compiler
6055

6156
#[repr(transparent)]
6257
pub struct T10(Sized, InternalIndirection<NonExhaustive>);
6358
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
64-
//~| WARN this was previously accepted by the compiler
6559

6660
#[repr(transparent)]
6761
pub struct T11(Sized, InternalIndirection<NonExhaustiveEnum>);
6862
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
69-
//~| WARN this was previously accepted by the compiler
7063

7164
#[repr(transparent)]
7265
pub struct T12(Sized, InternalIndirection<NonExhaustiveVariant>);
7366
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
74-
//~| WARN this was previously accepted by the compiler
7567

7668
#[repr(transparent)]
7769
pub struct T13(Sized, ExternalIndirection<Private>);
7870
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
79-
//~| WARN this was previously accepted by the compiler
8071

8172
#[repr(transparent)]
8273
pub struct T14(Sized, ExternalIndirection<NonExhaustive>);
8374
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
84-
//~| WARN this was previously accepted by the compiler
8575

8676
#[repr(transparent)]
8777
pub struct T15(Sized, ExternalIndirection<NonExhaustiveEnum>);
8878
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
89-
//~| WARN this was previously accepted by the compiler
9079

9180
#[repr(transparent)]
9281
pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
9382
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
94-
//~| WARN this was previously accepted by the compiler
9583

9684
#[repr(transparent)]
9785
pub struct T17(NonExhaustive, Sized);
9886
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
99-
//~| WARN this was previously accepted by the compiler
10087

10188
#[repr(transparent)]
10289
pub struct T18(NonExhaustive, NonExhaustive);
10390
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
104-
//~| WARN this was previously accepted by the compiler
10591

10692
#[repr(transparent)]
10793
pub struct T19(NonExhaustive, Private);
10894
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
109-
//~| WARN this was previously accepted by the compiler
11095

11196
#[repr(transparent)]
11297
pub struct T19Flipped(Private, NonExhaustive);
11398
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
114-
//~| WARN this was previously accepted by the compiler
11599

116100
#[repr(transparent)]
117101
pub struct T20(NonExhaustive);

0 commit comments

Comments
 (0)