-
Notifications
You must be signed in to change notification settings - Fork 13.9k
repr(transparent) check: do not compute check_unsuited more than once #148281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1508,22 +1508,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| { | ||
|
|
@@ -1532,60 +1520,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() } | ||
| return FieldInfo { span, trivial, ty }; | ||
| }); | ||
|
|
||
| let non_trivial_fields = field_infos | ||
|
|
@@ -1603,10 +1538,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() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pre-existing, but why does the function return a Perhaps this implementation detail should still be pushed inside with check_unsuited returning Option, while calling an internal function that returns ControlFlow? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is already a purely internal helper. I don't see the point of having even more nested functions here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. |
||
| { | ||
| // 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 { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still some code here we are running twice, in particular the
layout_ofquery. The alternative would be to simply collect the results into aVec, which will cost us an allocation but then avoids running the iterator twice.