@@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind};
1212use rustc_hir:: { LangItem , Node , attrs, find_attr, intravisit} ;
1313use rustc_infer:: infer:: { RegionVariableOrigin , TyCtxtInferExt } ;
1414use rustc_infer:: traits:: { Obligation , ObligationCauseCode , WellFormedLoc } ;
15- use rustc_lint_defs:: builtin:: {
16- REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS , UNSUPPORTED_CALLING_CONVENTIONS ,
17- } ;
15+ use rustc_lint_defs:: builtin:: UNSUPPORTED_CALLING_CONVENTIONS ;
1816use rustc_middle:: hir:: nested_filter;
1917use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
2018use rustc_middle:: middle:: stability:: EvalResult ;
@@ -1510,58 +1508,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15101508 return ;
15111509 }
15121510
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.
1511+ // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
15151512 let field_infos = adt. all_fields ( ) . map ( |field| {
15161513 let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
15171514 let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, field. did ) ;
15181515 let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
15191516 // We are currently checking the type this field came from, so it must be local
15201517 let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
15211518 let trivial = layout. is_ok_and ( |layout| layout. is_1zst ( ) ) ;
1522- if !trivial {
1523- return ( span, trivial, None ) ;
1524- }
1525- // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
1526-
1527- fn check_non_exhaustive < ' tcx > (
1528- 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) ,
1534- ty:: Adt ( def, args) => {
1535- if !def. did ( ) . is_local ( )
1536- && !find_attr ! (
1537- tcx. get_all_attrs( def. did( ) ) ,
1538- AttributeKind :: PubTransparent ( _)
1539- )
1540- {
1541- let non_exhaustive = def. is_variant_list_non_exhaustive ( )
1542- || def
1543- . variants ( )
1544- . iter ( )
1545- . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
1546- let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
1547- if non_exhaustive || has_priv {
1548- return ControlFlow :: Break ( (
1549- def. descr ( ) ,
1550- def. did ( ) ,
1551- args,
1552- non_exhaustive,
1553- ) ) ;
1554- }
1555- }
1556- def. all_fields ( )
1557- . map ( |field| field. ty ( tcx, args) )
1558- . try_for_each ( |t| check_non_exhaustive ( tcx, t) )
1559- }
1560- _ => ControlFlow :: Continue ( ( ) ) ,
1561- }
1562- }
1563-
1564- ( span, trivial, check_non_exhaustive ( tcx, ty) . break_value ( ) )
1519+ ( span, trivial, ty)
15651520 } ) ;
15661521
15671522 let non_trivial_fields = field_infos
@@ -1578,36 +1533,90 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15781533 ) ;
15791534 return ;
15801535 }
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 {
1536+
1537+ // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1538+ // fields or `repr(C)`. We call those fields "unsuited". Search for unsuited fields and
1539+ // error if the repr(transparent) condition relies on them.
1540+ enum UnsuitedReason {
1541+ NonExhaustive ,
1542+ PrivateField ,
1543+ ReprC ,
1544+ Uninhabited ,
1545+ }
1546+ struct UnsuitedInfo < ' tcx > {
1547+ ty : Ty < ' tcx > ,
1548+ reason : UnsuitedReason ,
1549+ }
1550+
1551+ fn check_unsuited_1zst < ' tcx > (
1552+ tcx : TyCtxt < ' tcx > ,
1553+ adt : DefId ,
1554+ ty : Ty < ' tcx > ,
1555+ ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
1556+ if !ty. is_inhabited_from ( tcx, adt, ty:: TypingEnv :: non_body_analysis ( tcx, adt) ) {
1557+ return ControlFlow :: Break ( UnsuitedInfo { ty, reason : UnsuitedReason :: Uninhabited } ) ;
1558+ }
1559+ match ty. kind ( ) {
1560+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited_1zst ( tcx, adt, t) ) ,
1561+ ty:: Array ( ty, _) => check_unsuited_1zst ( tcx, adt, * ty) ,
1562+ ty:: Adt ( def, args) => {
1563+ if !def. did ( ) . is_local ( )
1564+ && !find_attr ! ( tcx. get_all_attrs( def. did( ) ) , AttributeKind :: PubTransparent ( _) )
1565+ {
1566+ let non_exhaustive = def. is_variant_list_non_exhaustive ( )
1567+ || def. variants ( ) . iter ( ) . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
1568+ let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
1569+ if non_exhaustive || has_priv {
1570+ return ControlFlow :: Break ( UnsuitedInfo {
1571+ ty,
1572+ reason : if non_exhaustive {
1573+ UnsuitedReason :: NonExhaustive
1574+ } else {
1575+ UnsuitedReason :: PrivateField
1576+ } ,
1577+ } ) ;
1578+ }
1579+ }
1580+ if def. repr ( ) . c ( ) {
1581+ return ControlFlow :: Break ( UnsuitedInfo { ty, reason : UnsuitedReason :: ReprC } ) ;
1582+ }
1583+ def. all_fields ( )
1584+ . map ( |field| field. ty ( tcx, args) )
1585+ . try_for_each ( |t| check_unsuited_1zst ( tcx, adt, t) )
1586+ }
1587+ _ => ControlFlow :: Continue ( ( ) ) ,
1588+ }
1589+ }
1590+
1591+ let mut prev_unsuited_1zst = false ;
1592+ for ( span, trivial, ty) in field_infos {
1593+ if !trivial {
1594+ continue ;
1595+ }
1596+ if let Some ( unsuited) = check_unsuited_1zst ( tcx, adt. did ( ) , ty) . break_value ( ) {
15841597 // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15851598 // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1586- if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1587- tcx. node_span_lint (
1588- REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS ,
1589- tcx. local_def_id_to_hir_id ( adt. did ( ) . expect_local ( ) ) ,
1599+ if non_trivial_count > 0 || prev_unsuited_1zst {
1600+ let mut diag = tcx. dcx ( ) . struct_span_err (
15901601 span,
1591- |lint| {
1592- lint. primary_message (
1593- "zero-sized fields in `repr(transparent)` cannot \
1602+ "zero-sized fields in `repr(transparent)` cannot \
15941603 contain external non-exhaustive types",
1595- ) ;
1596- let note = if non_exhaustive {
1597- "is marked with `#[non_exhaustive]`"
1598- } else {
1599- "contains private fields"
1600- } ;
1601- let field_ty = tcx . def_path_str_with_args ( def_id , args ) ;
1602- lint . note ( format ! (
1603- "this {descr} contains `{field_ty}`, which {note}, \
1604+ ) ;
1605+ let note = match unsuited . reason {
1606+ UnsuitedReason :: NonExhaustive => "is marked with `#[non_exhaustive]`" ,
1607+ UnsuitedReason :: PrivateField => "contains private fields" ,
1608+ UnsuitedReason :: ReprC => "is marked with `#[repr(C)]`" ,
1609+ UnsuitedReason :: Uninhabited => "is not (publicly) inhabited" ,
1610+ } ;
1611+ diag . note ( format ! (
1612+ "this field contains `{field_ty}`, which {note}, \
16041613 and makes it not a breaking change to become \
1605- non-zero-sized in the future."
1606- ) ) ;
1607- } ,
1608- )
1614+ non-zero-sized in the future.",
1615+ field_ty = unsuited . ty ,
1616+ ) ) ;
1617+ diag . emit ( ) ;
16091618 } else {
1610- prev_non_exhaustive_1zst = true ;
1619+ prev_unsuited_1zst = true ;
16111620 }
16121621 }
16131622 }
0 commit comments