@@ -11,7 +11,7 @@ use rustc_hir::def::{CtorKind, DefKind};
1111use rustc_hir:: { LangItem , Node , find_attr, intravisit} ;
1212use rustc_infer:: infer:: { RegionVariableOrigin , TyCtxtInferExt } ;
1313use rustc_infer:: traits:: { Obligation , ObligationCauseCode , WellFormedLoc } ;
14- use rustc_lint_defs:: builtin:: { REPR_TRANSPARENT_NON_ZST_FIELDS , UNSUPPORTED_CALLING_CONVENTIONS } ;
14+ use rustc_lint_defs:: builtin:: UNSUPPORTED_CALLING_CONVENTIONS ;
1515use rustc_macros:: Diagnostic ;
1616use rustc_middle:: hir:: nested_filter;
1717use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
@@ -1689,39 +1689,6 @@ pub(super) fn check_packed_inner(
16891689}
16901690
16911691pub ( super ) fn check_transparent < ' tcx > ( tcx : TyCtxt < ' tcx > , adt : ty:: AdtDef < ' tcx > ) {
1692- struct ZeroSizedFieldReprTransparentIncompatibility < ' tcx > {
1693- unsuited : UnsuitedInfo < ' tcx > ,
1694- }
1695-
1696- impl < ' a , ' tcx > Diagnostic < ' a , ( ) > for ZeroSizedFieldReprTransparentIncompatibility < ' tcx > {
1697- fn into_diag ( self , dcx : DiagCtxtHandle < ' a > , level : Level ) -> Diag < ' a , ( ) > {
1698- let Self { unsuited } = self ;
1699- let ( title, note) = match unsuited. reason {
1700- UnsuitedReason :: NonExhaustive => (
1701- "external non-exhaustive types" ,
1702- "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future." ,
1703- ) ,
1704- UnsuitedReason :: PrivateField => (
1705- "external types with private fields" ,
1706- "contains private fields, so it could become non-zero-sized in the future." ,
1707- ) ,
1708- UnsuitedReason :: ReprC => (
1709- "`repr(C)` types" ,
1710- "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets." ,
1711- ) ,
1712- } ;
1713- Diag :: new (
1714- dcx,
1715- level,
1716- format ! ( "zero-sized fields in `repr(transparent)` cannot contain {title}" ) ,
1717- )
1718- . with_note ( format ! (
1719- "this field contains `{field_ty}`, which {note}" ,
1720- field_ty = unsuited. ty,
1721- ) )
1722- }
1723- }
1724-
17251692 if !adt. repr ( ) . transparent ( ) {
17261693 return ;
17271694 }
@@ -1741,108 +1708,145 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
17411708 // Don't bother checking the fields.
17421709 return ;
17431710 }
1711+ let variant = adt. variant ( VariantIdx :: ZERO ) ;
17441712
1745- let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, adt. did ( ) ) ;
1746- // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1747- struct FieldInfo < ' tcx > {
1748- span : Span ,
1749- trivial : bool ,
1750- ty : Ty < ' tcx > ,
1751- }
1752-
1753- let field_infos = adt. all_fields ( ) . map ( |field| {
1754- let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
1755- let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
1756- // We are currently checking the type this field came from, so it must be local
1757- let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
1758- let trivial = layout. is_ok_and ( |layout| layout. is_1zst ( ) ) ;
1759- FieldInfo { span, trivial, ty }
1760- } ) ;
1761-
1762- let non_trivial_fields = field_infos
1763- . clone ( )
1764- . filter_map ( |field| if !field. trivial { Some ( field. span ) } else { None } ) ;
1765- let non_trivial_count = non_trivial_fields. clone ( ) . count ( ) ;
1766- if non_trivial_count >= 2 {
1767- bad_non_zero_sized_fields (
1768- tcx,
1769- adt,
1770- non_trivial_count,
1771- non_trivial_fields,
1772- tcx. def_span ( adt. did ( ) ) ,
1773- ) ;
1713+ if variant. fields . len ( ) <= 1 {
1714+ // No need to check when there's at most one field.
17741715 return ;
17751716 }
17761717
1777- // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1778- // fields or `repr(C)`. We call those fields "unsuited".
1779- struct UnsuitedInfo < ' tcx > {
1780- /// The source of the problem, a type that is found somewhere within the field type.
1781- ty : Ty < ' tcx > ,
1782- reason : UnsuitedReason ,
1783- }
1784- enum UnsuitedReason {
1785- NonExhaustive ,
1786- PrivateField ,
1787- ReprC ,
1718+ let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, adt. did ( ) ) ;
1719+
1720+ /// We call a field "trivial" for `repr(transparent)` purposes if it can be ignored.
1721+ /// IOW, `repr(transparent)` is allowed if there is at most one non-trivial field.
1722+ /// This enum captuers all the reasons why a field might not be "trivial".
1723+ enum NonTrivialReason < ' tcx > {
1724+ UnknownLayout ,
1725+ NonZeroSized ,
1726+ NonTrivialAlignment ,
1727+ PrivateField { inside : Ty < ' tcx > } ,
1728+ NonExhaustive { ty : Ty < ' tcx > } ,
1729+ ReprC { ty : Ty < ' tcx > } ,
1730+ Array ,
1731+ }
1732+ struct NonTrivialFieldInfo < ' tcx > {
1733+ span : Span ,
1734+ reason : NonTrivialReason < ' tcx > ,
17881735 }
17891736
1790- fn check_unsuited < ' tcx > (
1737+ /// Check if this type is "trivial" for `repr(transparent)`. If not, return the reason why
1738+ /// and the problematic type.
1739+ fn is_trivial < ' tcx > (
17911740 tcx : TyCtxt < ' tcx > ,
17921741 typing_env : ty:: TypingEnv < ' tcx > ,
17931742 ty : Ty < ' tcx > ,
1794- ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
1743+ ) -> ControlFlow < NonTrivialReason < ' tcx > > {
17951744 // We can encounter projections during traversal, so ensure the type is normalized.
17961745 let ty =
17971746 tcx. try_normalize_erasing_regions ( typing_env, Unnormalized :: new_wip ( ty) ) . unwrap_or ( ty) ;
17981747 match ty. kind ( ) {
1799- ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited ( tcx, typing_env, t) ) ,
1800- ty:: Array ( ty, _) => check_unsuited ( tcx, typing_env, * ty) ,
1748+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| is_trivial ( tcx, typing_env, t) ) ,
1749+ ty:: Array ( ..) => {
1750+ // Arrays are meant to be ABI-compatible with what happens in other languages,
1751+ // so we should not promise that they are "trivial".
1752+ return ControlFlow :: Break ( NonTrivialReason :: Array ) ;
1753+ }
18011754 ty:: Adt ( def, args) => {
18021755 if !def. did ( ) . is_local ( ) && !find_attr ! ( tcx, def. did( ) , RustcPubTransparent ( _) ) {
18031756 let non_exhaustive = def. is_variant_list_non_exhaustive ( )
18041757 || def. variants ( ) . iter ( ) . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
1758+ if non_exhaustive {
1759+ return ControlFlow :: Break ( NonTrivialReason :: NonExhaustive { ty } ) ;
1760+ }
18051761 let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
1806- if non_exhaustive || has_priv {
1807- return ControlFlow :: Break ( UnsuitedInfo {
1808- ty,
1809- reason : if non_exhaustive {
1810- UnsuitedReason :: NonExhaustive
1811- } else {
1812- UnsuitedReason :: PrivateField
1813- } ,
1814- } ) ;
1762+ if has_priv {
1763+ return ControlFlow :: Break ( NonTrivialReason :: PrivateField { inside : ty } ) ;
18151764 }
18161765 }
18171766 if def. repr ( ) . c ( ) {
1818- return ControlFlow :: Break ( UnsuitedInfo { ty, reason : UnsuitedReason :: ReprC } ) ;
1767+ return ControlFlow :: Break ( NonTrivialReason :: ReprC { ty } ) ;
18191768 }
18201769 def. all_fields ( )
18211770 . map ( |field| field. ty ( tcx, args) )
1822- . try_for_each ( |t| check_unsuited ( tcx, typing_env, t) )
1771+ . try_for_each ( |t| is_trivial ( tcx, typing_env, t) )
18231772 }
18241773 _ => ControlFlow :: Continue ( ( ) ) ,
18251774 }
18261775 }
18271776
1828- let mut prev_unsuited_1zst = false ;
1829- for field in field_infos {
1830- if field. trivial
1831- && let Some ( unsuited) = check_unsuited ( tcx, typing_env, field. ty ) . break_value ( )
1832- {
1833- // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
1834- // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1835- if non_trivial_count > 0 || prev_unsuited_1zst {
1836- tcx. emit_node_span_lint (
1837- REPR_TRANSPARENT_NON_ZST_FIELDS ,
1838- tcx. local_def_id_to_hir_id ( adt. did ( ) . expect_local ( ) ) ,
1839- field. span ,
1840- ZeroSizedFieldReprTransparentIncompatibility { unsuited } ,
1841- ) ;
1842- } else {
1843- prev_unsuited_1zst = true ;
1777+ let non_trivial_fields = variant
1778+ . fields
1779+ . iter ( )
1780+ . filter_map ( |field| {
1781+ let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
1782+ let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
1783+ // We are currently checking the type this field came from, so it must be local
1784+ let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
1785+ // Rule out non-1ZST
1786+ if !layout. is_ok_and ( |layout| layout. is_1zst ( ) ) {
1787+ let reason = match layout {
1788+ Err ( _) => NonTrivialReason :: UnknownLayout ,
1789+ Ok ( layout) => {
1790+ if !( layout. is_sized ( ) && layout. size . bytes ( ) == 0 ) {
1791+ NonTrivialReason :: NonZeroSized
1792+ } else {
1793+ NonTrivialReason :: NonTrivialAlignment
1794+ }
1795+ }
1796+ } ;
1797+ return Some ( NonTrivialFieldInfo { span, reason } ) ;
1798+ }
1799+ // Recursively check for other things that have to be ruled out.
1800+ if let Some ( reason) = is_trivial ( tcx, typing_env, ty) . break_value ( ) {
1801+ return Some ( NonTrivialFieldInfo { span, reason } ) ;
18441802 }
1803+ // Otherwise,
1804+ None
1805+ } )
1806+ . collect :: < Vec < _ > > ( ) ;
1807+
1808+ if non_trivial_fields. len ( ) >= 2 {
1809+ let count = non_trivial_fields. len ( ) ;
1810+ let desc = if adt. is_enum ( ) {
1811+ format_args ! ( "the variant of a transparent {}" , adt. descr( ) )
1812+ } else {
1813+ format_args ! ( "transparent {}" , adt. descr( ) )
1814+ } ;
1815+ let ty_span = tcx. def_span ( adt. did ( ) ) ;
1816+ let mut diag = tcx. dcx ( ) . struct_span_err (
1817+ ty_span,
1818+ format ! ( "{desc} needs at most one non-trivial field, but has {count}" ) ,
1819+ ) ;
1820+ diag. code ( E0690 ) ;
1821+
1822+ // Label for the type.
1823+ diag. span_label ( ty_span, format ! ( "needs at most one non-trivial field, but has {count}" ) ) ;
1824+ // Label for each non-trivial field.
1825+ for field in non_trivial_fields {
1826+ let msg = match field. reason {
1827+ NonTrivialReason :: UnknownLayout => {
1828+ format ! ( "this field is generic and hence may have non-zero size" )
1829+ }
1830+ NonTrivialReason :: NonZeroSized => format ! ( "this field has non-zero size" ) ,
1831+ NonTrivialReason :: NonTrivialAlignment => format ! ( "this field requires alignment" ) ,
1832+ NonTrivialReason :: PrivateField { inside } => format ! (
1833+ "this field contains `{inside}`, which has private fields, so it could become non-zero-sized in the future"
1834+ ) ,
1835+ NonTrivialReason :: NonExhaustive { ty } => format ! (
1836+ "this field contains `{ty}`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future"
1837+ ) ,
1838+ NonTrivialReason :: ReprC { ty } => format ! (
1839+ "this field contains `{ty}`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets"
1840+ ) ,
1841+ NonTrivialReason :: Array => format ! (
1842+ "this field contains an array, which might be relevant for the ABI on some targets"
1843+ ) ,
1844+ } ;
1845+ diag. span_label ( field. span , msg) ;
18451846 }
1847+
1848+ diag. emit ( ) ;
1849+ return ;
18461850 }
18471851}
18481852
0 commit comments