repr(transparent) check: do not compute check_unsuited more than once
This commit is contained in:
parent
42ebbd2356
commit
c23182eb24
1 changed files with 56 additions and 68 deletions
|
|
@ -1542,22 +1542,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
|
||||
let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did());
|
||||
// For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
|
||||
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
|
||||
// fields or `repr(C)`. We call those fields "unsuited".
|
||||
struct FieldInfo<'tcx> {
|
||||
span: Span,
|
||||
trivial: bool,
|
||||
unsuited: Option<UnsuitedInfo<'tcx>>,
|
||||
}
|
||||
struct UnsuitedInfo<'tcx> {
|
||||
/// The source of the problem, a type that is found somewhere within the field type.
|
||||
ty: Ty<'tcx>,
|
||||
reason: UnsuitedReason,
|
||||
}
|
||||
enum UnsuitedReason {
|
||||
NonExhaustive,
|
||||
PrivateField,
|
||||
ReprC,
|
||||
}
|
||||
|
||||
let field_infos = adt.all_fields().map(|field| {
|
||||
|
|
@ -1566,60 +1554,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
// We are currently checking the type this field came from, so it must be local
|
||||
let span = tcx.hir_span_if_local(field.did).unwrap();
|
||||
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
|
||||
if !trivial {
|
||||
// No need to even compute `unsuited`.
|
||||
return FieldInfo { span, trivial, unsuited: None };
|
||||
}
|
||||
|
||||
fn check_unsuited<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> ControlFlow<UnsuitedInfo<'tcx>> {
|
||||
// We can encounter projections during traversal, so ensure the type is normalized.
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
match ty.kind() {
|
||||
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)),
|
||||
ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty),
|
||||
ty::Adt(def, args) => {
|
||||
if !def.did().is_local()
|
||||
&& !find_attr!(
|
||||
tcx.get_all_attrs(def.did()),
|
||||
AttributeKind::PubTransparent(_)
|
||||
)
|
||||
{
|
||||
let non_exhaustive = def.is_variant_list_non_exhaustive()
|
||||
|| def
|
||||
.variants()
|
||||
.iter()
|
||||
.any(ty::VariantDef::is_field_list_non_exhaustive);
|
||||
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
|
||||
if non_exhaustive || has_priv {
|
||||
return ControlFlow::Break(UnsuitedInfo {
|
||||
ty,
|
||||
reason: if non_exhaustive {
|
||||
UnsuitedReason::NonExhaustive
|
||||
} else {
|
||||
UnsuitedReason::PrivateField
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
if def.repr().c() {
|
||||
return ControlFlow::Break(UnsuitedInfo {
|
||||
ty,
|
||||
reason: UnsuitedReason::ReprC,
|
||||
});
|
||||
}
|
||||
def.all_fields()
|
||||
.map(|field| field.ty(tcx, args))
|
||||
.try_for_each(|t| check_unsuited(tcx, typing_env, t))
|
||||
}
|
||||
_ => ControlFlow::Continue(()),
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo { span, trivial, unsuited: check_unsuited(tcx, typing_env, ty).break_value() }
|
||||
FieldInfo { span, trivial, ty }
|
||||
});
|
||||
|
||||
let non_trivial_fields = field_infos
|
||||
|
|
@ -1637,10 +1572,63 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
|||
return;
|
||||
}
|
||||
|
||||
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
|
||||
// fields or `repr(C)`. We call those fields "unsuited".
|
||||
struct UnsuitedInfo<'tcx> {
|
||||
/// The source of the problem, a type that is found somewhere within the field type.
|
||||
ty: Ty<'tcx>,
|
||||
reason: UnsuitedReason,
|
||||
}
|
||||
enum UnsuitedReason {
|
||||
NonExhaustive,
|
||||
PrivateField,
|
||||
ReprC,
|
||||
}
|
||||
|
||||
fn check_unsuited<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> ControlFlow<UnsuitedInfo<'tcx>> {
|
||||
// We can encounter projections during traversal, so ensure the type is normalized.
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
match ty.kind() {
|
||||
ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)),
|
||||
ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty),
|
||||
ty::Adt(def, args) => {
|
||||
if !def.did().is_local()
|
||||
&& !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_))
|
||||
{
|
||||
let non_exhaustive = def.is_variant_list_non_exhaustive()
|
||||
|| def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive);
|
||||
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
|
||||
if non_exhaustive || has_priv {
|
||||
return ControlFlow::Break(UnsuitedInfo {
|
||||
ty,
|
||||
reason: if non_exhaustive {
|
||||
UnsuitedReason::NonExhaustive
|
||||
} else {
|
||||
UnsuitedReason::PrivateField
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
if def.repr().c() {
|
||||
return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::ReprC });
|
||||
}
|
||||
def.all_fields()
|
||||
.map(|field| field.ty(tcx, args))
|
||||
.try_for_each(|t| check_unsuited(tcx, typing_env, t))
|
||||
}
|
||||
_ => ControlFlow::Continue(()),
|
||||
}
|
||||
}
|
||||
|
||||
let mut prev_unsuited_1zst = false;
|
||||
for field in field_infos {
|
||||
if let Some(unsuited) = field.unsuited {
|
||||
assert!(field.trivial);
|
||||
if field.trivial
|
||||
&& let Some(unsuited) = check_unsuited(tcx, typing_env, field.ty).break_value()
|
||||
{
|
||||
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
|
||||
// Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
|
||||
if non_trivial_count > 0 || prev_unsuited_1zst {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue