Skip to content

Commit 28ced79

Browse files
committed
repr(transparent): do not consider repr(C) types to be 1-ZST
1 parent a2db928 commit 28ced79

File tree

6 files changed

+146
-51
lines changed

6 files changed

+146
-51
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,8 +1510,25 @@ 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+
}
1531+
15151532
let field_infos = adt.all_fields().map(|field| {
15161533
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
15171534
let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did);
@@ -1520,17 +1537,17 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15201537
let span = tcx.hir_span_if_local(field.did).unwrap();
15211538
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
15221539
if !trivial {
1523-
return (span, trivial, None);
1540+
// No need to even compute `unsuited`.
1541+
return FieldInfo { span, trivial, unsuited: None };
15241542
}
1525-
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
15261543

1527-
fn check_non_exhaustive<'tcx>(
1544+
fn check_unsuited<'tcx>(
15281545
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),
1546+
ty: Ty<'tcx>,
1547+
) -> ControlFlow<UnsuitedInfo<'tcx>> {
1548+
match ty.kind() {
1549+
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, t)),
1550+
ty::Array(ty, _) => check_unsuited(tcx, *ty),
15341551
ty::Adt(def, args) => {
15351552
if !def.did().is_local()
15361553
&& !find_attr!(
@@ -1545,28 +1562,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15451562
.any(ty::VariantDef::is_field_list_non_exhaustive);
15461563
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
15471564
if non_exhaustive || has_priv {
1548-
return ControlFlow::Break((
1549-
def.descr(),
1550-
def.did(),
1551-
args,
1552-
non_exhaustive,
1553-
));
1565+
return ControlFlow::Break(UnsuitedInfo {
1566+
ty,
1567+
reason: if non_exhaustive {
1568+
UnsuitedReason::NonExhaustive
1569+
} else {
1570+
UnsuitedReason::PrivateField
1571+
},
1572+
});
15541573
}
15551574
}
1575+
if def.repr().c() {
1576+
return ControlFlow::Break(UnsuitedInfo {
1577+
ty,
1578+
reason: UnsuitedReason::ReprC,
1579+
});
1580+
}
15561581
def.all_fields()
15571582
.map(|field| field.ty(tcx, args))
1558-
.try_for_each(|t| check_non_exhaustive(tcx, t))
1583+
.try_for_each(|t| check_unsuited(tcx, t))
15591584
}
15601585
_ => ControlFlow::Continue(()),
15611586
}
15621587
}
15631588

1564-
(span, trivial, check_non_exhaustive(tcx, ty).break_value())
1589+
FieldInfo { span, trivial, unsuited: check_unsuited(tcx, ty).break_value() }
15651590
});
15661591

15671592
let non_trivial_fields = field_infos
15681593
.clone()
1569-
.filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
1594+
.filter_map(|field| if !field.trivial { Some(field.span) } else { None });
15701595
let non_trivial_count = non_trivial_fields.clone().count();
15711596
if non_trivial_count >= 2 {
15721597
bad_non_zero_sized_fields(
@@ -1578,36 +1603,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15781603
);
15791604
return;
15801605
}
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 {
1606+
1607+
let mut prev_unsuited_1zst = false;
1608+
for field in field_infos {
1609+
if let Some(unsuited) = field.unsuited {
1610+
assert!(field.trivial);
15841611
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15851612
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1586-
if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1613+
if non_trivial_count > 0 || prev_unsuited_1zst {
15871614
tcx.node_span_lint(
15881615
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
15891616
tcx.local_def_id_to_hir_id(adt.did().expect_local()),
1590-
span,
1617+
field.span,
15911618
|lint| {
15921619
lint.primary_message(
15931620
"zero-sized fields in `repr(transparent)` cannot \
15941621
contain external non-exhaustive types",
15951622
);
1596-
let note = if non_exhaustive {
1597-
"is marked with `#[non_exhaustive]`"
1598-
} else {
1599-
"contains private fields"
1623+
let note = match unsuited.reason {
1624+
UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.",
1625+
UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.",
1626+
UnsuitedReason::ReprC => "is marked with `#[repr(C)]`, which means it cannot be ignored for all ABIs.",
16001627
};
1601-
let field_ty = tcx.def_path_str_with_args(def_id, args);
16021628
lint.note(format!(
1603-
"this {descr} contains `{field_ty}`, which {note}, \
1604-
and makes it not a breaking change to become \
1605-
non-zero-sized in the future."
1629+
"this field contains `{field_ty}`, which {note}",
1630+
field_ty = unsuited.ty,
16061631
));
16071632
},
1608-
)
1633+
);
16091634
} else {
1610-
prev_non_exhaustive_1zst = true;
1635+
prev_unsuited_1zst = true;
16111636
}
16121637
}
16131638
}

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.stderr

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | pub struct T5(Sized, Private);
66
|
77
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
88
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
9-
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
9+
= note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future.
1010
note: the lint level is defined here
1111
--> $DIR/repr-transparent-non-exhaustive.rs:1:9
1212
|
@@ -21,7 +21,7 @@ LL | pub struct T6(Sized, NonExhaustive);
2121
|
2222
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
2323
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
24-
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
24+
= note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
2525

2626
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
2727
--> $DIR/repr-transparent-non-exhaustive.rs:47:22
@@ -31,7 +31,7 @@ LL | pub struct T7(Sized, NonExhaustiveEnum);
3131
|
3232
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
3333
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
34-
= note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
34+
= note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
3535

3636
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
3737
--> $DIR/repr-transparent-non-exhaustive.rs:52:22
@@ -41,7 +41,7 @@ LL | pub struct T8(Sized, NonExhaustiveVariant);
4141
|
4242
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
4343
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
44-
= note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
44+
= note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
4545

4646
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
4747
--> $DIR/repr-transparent-non-exhaustive.rs:57:22
@@ -51,7 +51,7 @@ LL | pub struct T9(Sized, InternalIndirection<Private>);
5151
|
5252
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
5353
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
54-
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
54+
= note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future.
5555

5656
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
5757
--> $DIR/repr-transparent-non-exhaustive.rs:62:23
@@ -61,7 +61,7 @@ LL | pub struct T10(Sized, InternalIndirection<NonExhaustive>);
6161
|
6262
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
6363
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
64-
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
64+
= note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
6565

6666
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
6767
--> $DIR/repr-transparent-non-exhaustive.rs:67:23
@@ -71,7 +71,7 @@ LL | pub struct T11(Sized, InternalIndirection<NonExhaustiveEnum>);
7171
|
7272
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
7373
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
74-
= note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
74+
= note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
7575

7676
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
7777
--> $DIR/repr-transparent-non-exhaustive.rs:72:23
@@ -81,7 +81,7 @@ LL | pub struct T12(Sized, InternalIndirection<NonExhaustiveVariant>);
8181
|
8282
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8383
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
84-
= note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
84+
= note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
8585

8686
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
8787
--> $DIR/repr-transparent-non-exhaustive.rs:77:23
@@ -91,7 +91,7 @@ LL | pub struct T13(Sized, ExternalIndirection<Private>);
9191
|
9292
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
9393
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
94-
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
94+
= note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future.
9595

9696
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
9797
--> $DIR/repr-transparent-non-exhaustive.rs:82:23
@@ -101,7 +101,7 @@ LL | pub struct T14(Sized, ExternalIndirection<NonExhaustive>);
101101
|
102102
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
103103
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
104-
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
104+
= note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
105105

106106
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
107107
--> $DIR/repr-transparent-non-exhaustive.rs:87:23
@@ -111,7 +111,7 @@ LL | pub struct T15(Sized, ExternalIndirection<NonExhaustiveEnum>);
111111
|
112112
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
113113
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
114-
= note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
114+
= note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
115115

116116
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
117117
--> $DIR/repr-transparent-non-exhaustive.rs:92:23
@@ -121,7 +121,7 @@ LL | pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
121121
|
122122
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
123123
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
124-
= note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
124+
= note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
125125

126126
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
127127
--> $DIR/repr-transparent-non-exhaustive.rs:97:16
@@ -131,7 +131,7 @@ LL | pub struct T17(NonExhaustive, Sized);
131131
|
132132
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
133133
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
134-
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
134+
= note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
135135

136136
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
137137
--> $DIR/repr-transparent-non-exhaustive.rs:102:31
@@ -141,7 +141,7 @@ LL | pub struct T18(NonExhaustive, NonExhaustive);
141141
|
142142
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
143143
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
144-
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
144+
= note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
145145

146146
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
147147
--> $DIR/repr-transparent-non-exhaustive.rs:107:31
@@ -151,7 +151,7 @@ LL | pub struct T19(NonExhaustive, Private);
151151
|
152152
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
153153
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
154-
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
154+
= note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future.
155155

156156
error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
157157
--> $DIR/repr-transparent-non-exhaustive.rs:112:32
@@ -161,7 +161,7 @@ LL | pub struct T19Flipped(Private, NonExhaustive);
161161
|
162162
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
163163
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
164-
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
164+
= note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
165165

166166
error: aborting due to 16 previous errors
167167

0 commit comments

Comments
 (0)