Skip to content

Commit 3a5a811

Browse files
Auto merge of #148243 - RalfJung:perf-transparent-checks, r=<try>
perf experiment for #147185
2 parents 292db4a + 60765e3 commit 3a5a811

File tree

13 files changed

+112
-570
lines changed

13 files changed

+112
-570
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 32 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ 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::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS};
15+
use rustc_lint_defs::builtin::{
16+
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS,
17+
};
1618
use rustc_middle::hir::nested_filter;
1719
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
1820
use rustc_middle::middle::stability::EvalResult;
@@ -1507,41 +1509,24 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15071509
}
15081510

15091511
let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did());
1510-
// For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1511-
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1512-
// fields or `repr(C)`. We call those fields "unsuited".
1513-
struct FieldInfo<'tcx> {
1514-
span: Span,
1515-
trivial: bool,
1516-
unsuited: Option<UnsuitedInfo<'tcx>>,
1517-
}
1518-
struct UnsuitedInfo<'tcx> {
1519-
/// The source of the problem, a type that is found somewhere within the field type.
1520-
ty: Ty<'tcx>,
1521-
reason: UnsuitedReason,
1522-
}
1523-
enum UnsuitedReason {
1524-
NonExhaustive,
1525-
PrivateField,
1526-
ReprC,
1527-
}
1528-
1512+
// For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
1513+
// "known" respecting #[non_exhaustive] attributes.
15291514
let field_infos = adt.all_fields().map(|field| {
15301515
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
15311516
let layout = tcx.layout_of(typing_env.as_query_input(ty));
15321517
// We are currently checking the type this field came from, so it must be local
15331518
let span = tcx.hir_span_if_local(field.did).unwrap();
15341519
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
15351520
if !trivial {
1536-
// No need to even compute `unsuited`.
1537-
return FieldInfo { span, trivial, unsuited: None };
1521+
return (span, trivial, None);
15381522
}
1523+
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
15391524

15401525
fn check_unsuited<'tcx>(
15411526
tcx: TyCtxt<'tcx>,
15421527
typing_env: ty::TypingEnv<'tcx>,
15431528
ty: Ty<'tcx>,
1544-
) -> ControlFlow<UnsuitedInfo<'tcx>> {
1529+
) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> {
15451530
// We can encounter projections during traversal, so ensure the type is normalized.
15461531
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
15471532
match ty.kind() {
@@ -1561,21 +1546,16 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15611546
.any(ty::VariantDef::is_field_list_non_exhaustive);
15621547
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
15631548
if non_exhaustive || has_priv {
1564-
return ControlFlow::Break(UnsuitedInfo {
1565-
ty,
1566-
reason: if non_exhaustive {
1567-
UnsuitedReason::NonExhaustive
1568-
} else {
1569-
UnsuitedReason::PrivateField
1570-
},
1571-
});
1549+
return ControlFlow::Break((
1550+
def.descr(),
1551+
def.did(),
1552+
args,
1553+
non_exhaustive,
1554+
));
15721555
}
15731556
}
15741557
if def.repr().c() {
1575-
return ControlFlow::Break(UnsuitedInfo {
1576-
ty,
1577-
reason: UnsuitedReason::ReprC,
1578-
});
1558+
return ControlFlow::Break((def.descr(), def.did(), args, true));
15791559
}
15801560
def.all_fields()
15811561
.map(|field| field.ty(tcx, args))
@@ -1585,12 +1565,12 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15851565
}
15861566
}
15871567

1588-
FieldInfo { span, trivial, unsuited: check_unsuited(tcx, typing_env, ty).break_value() }
1568+
(span, trivial, check_unsuited(tcx, typing_env, ty).break_value())
15891569
});
15901570

15911571
let non_trivial_fields = field_infos
15921572
.clone()
1593-
.filter_map(|field| if !field.trivial { Some(field.span) } else { None });
1573+
.filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
15941574
let non_trivial_count = non_trivial_fields.clone().count();
15951575
if non_trivial_count >= 2 {
15961576
bad_non_zero_sized_fields(
@@ -1602,38 +1582,34 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
16021582
);
16031583
return;
16041584
}
1605-
16061585
let mut prev_unsuited_1zst = false;
1607-
for field in field_infos {
1608-
if let Some(unsuited) = field.unsuited {
1609-
assert!(field.trivial);
1586+
for (span, _trivial, non_exhaustive_1zst) in field_infos {
1587+
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst {
16101588
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
16111589
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
16121590
if non_trivial_count > 0 || prev_unsuited_1zst {
16131591
tcx.node_span_lint(
1614-
REPR_TRANSPARENT_NON_ZST_FIELDS,
1592+
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
16151593
tcx.local_def_id_to_hir_id(adt.did().expect_local()),
1616-
field.span,
1594+
span,
16171595
|lint| {
1618-
let title = match unsuited.reason {
1619-
UnsuitedReason::NonExhaustive => "external non-exhaustive types",
1620-
UnsuitedReason::PrivateField => "external types with private fields",
1621-
UnsuitedReason::ReprC => "`repr(C)` types",
1622-
};
16231596
lint.primary_message(
1624-
format!("zero-sized fields in `repr(transparent)` cannot contain {title}"),
1597+
"zero-sized fields in `repr(transparent)` cannot \
1598+
contain external non-exhaustive types",
16251599
);
1626-
let note = match unsuited.reason {
1627-
UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.",
1628-
UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.",
1629-
UnsuitedReason::ReprC => "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.",
1600+
let note = if non_exhaustive {
1601+
"is marked with `#[non_exhaustive]`"
1602+
} else {
1603+
"contains private fields"
16301604
};
1605+
let field_ty = tcx.def_path_str_with_args(def_id, args);
16311606
lint.note(format!(
1632-
"this field contains `{field_ty}`, which {note}",
1633-
field_ty = unsuited.ty,
1607+
"this {descr} contains `{field_ty}`, which {note}, \
1608+
and makes it not a breaking change to become \
1609+
non-zero-sized in the future."
16341610
));
16351611
},
1636-
);
1612+
)
16371613
} else {
16381614
prev_unsuited_1zst = true;
16391615
}

compiler/rustc_lint/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,6 @@ fn register_builtins(store: &mut LintStore) {
362362
store.register_renamed("static_mut_ref", "static_mut_refs");
363363
store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries");
364364
store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes");
365-
store.register_renamed(
366-
"repr_transparent_external_private_fields",
367-
"repr_transparent_non_zst_fields",
368-
);
369365

370366
// These were moved to tool lints, but rustc still sees them when compiling normally, before
371367
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ declare_lint_pass! {
8686
REFINING_IMPL_TRAIT_INTERNAL,
8787
REFINING_IMPL_TRAIT_REACHABLE,
8888
RENAMED_AND_REMOVED_LINTS,
89-
REPR_TRANSPARENT_NON_ZST_FIELDS,
89+
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
9090
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
9191
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
9292
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
@@ -3011,23 +3011,19 @@ declare_lint! {
30113011
}
30123012

30133013
declare_lint! {
3014-
/// The `repr_transparent_non_zst_fields` lint
3014+
/// The `repr_transparent_external_private_fields` lint
30153015
/// detects types marked `#[repr(transparent)]` that (transitively)
3016-
/// contain a type that is not guaranteed to remain a ZST type under all configurations.
3016+
/// contain an external ZST type marked `#[non_exhaustive]` or containing
3017+
/// private fields
30173018
///
30183019
/// ### Example
30193020
///
30203021
/// ```rust,ignore (needs external crate)
30213022
/// #![deny(repr_transparent_external_private_fields)]
30223023
/// use foo::NonExhaustiveZst;
30233024
///
3024-
/// #[repr(C)]
3025-
/// struct CZst([u8; 0]);
3026-
///
30273025
/// #[repr(transparent)]
30283026
/// struct Bar(u32, ([u32; 0], NonExhaustiveZst));
3029-
/// #[repr(transparent)]
3030-
/// struct Baz(u32, CZst);
30313027
/// ```
30323028
///
30333029
/// This will produce:
@@ -3046,39 +3042,26 @@ declare_lint! {
30463042
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30473043
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
30483044
/// = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
3049-
/// = note: this field contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.
3050-
///
3051-
/// error: zero-sized fields in repr(transparent) cannot contain `#[repr(C)]` types
3052-
/// --> src/main.rs:5:28
3053-
/// |
3054-
/// 5 | struct Baz(u32, CZst);
3055-
/// | ^^^^
3056-
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
3057-
/// = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
3058-
/// = note: this field contains `CZst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.
3045+
/// = note: this struct contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
30593046
/// ```
30603047
///
30613048
/// ### Explanation
30623049
///
3063-
/// Previous, Rust accepted fields that contain external private zero-sized types, even though
3064-
/// those types could gain a non-zero-sized field in a future, semver-compatible update.
3065-
///
3066-
/// Rust also accepted fields that contain `repr(C)` zero-sized types, even though those types
3067-
/// are not guaranteed to be zero-sized on all targets, and even though those types can
3068-
/// make a difference for the ABI (and therefore cannot be ignored by `repr(transparent)`).
3050+
/// Previous, Rust accepted fields that contain external private zero-sized types,
3051+
/// even though it should not be a breaking change to add a non-zero-sized field to
3052+
/// that private type.
30693053
///
30703054
/// This is a [future-incompatible] lint to transition this
30713055
/// to a hard error in the future. See [issue #78586] for more details.
30723056
///
30733057
/// [issue #78586]: https://github.com/rust-lang/rust/issues/78586
30743058
/// [future-incompatible]: ../index.md#future-incompatible-lints
3075-
pub REPR_TRANSPARENT_NON_ZST_FIELDS,
3076-
Deny,
3059+
pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
3060+
Warn,
30773061
"transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
30783062
@future_incompatible = FutureIncompatibleInfo {
30793063
reason: FutureIncompatibilityReason::FutureReleaseError,
30803064
reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
3081-
report_in_deps: true,
30823065
};
30833066
}
30843067

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 its documentation for more guidance)
406+
/// (see it's 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/lint/improper-ctypes/lint-ctypes.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>);
3838
#[repr(transparent)]
3939
pub struct TransparentUnit<U>(f32, PhantomData<U>);
4040
#[repr(transparent)]
41-
#[allow(repr_transparent_non_zst_fields)]
4241
pub struct TransparentCustomZst(i32, ZeroSize);
4342

4443
#[repr(C)]

0 commit comments

Comments
 (0)