@@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind};
12
12
use rustc_hir:: { LangItem , Node , attrs, find_attr, intravisit} ;
13
13
use rustc_infer:: infer:: { RegionVariableOrigin , TyCtxtInferExt } ;
14
14
use 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 ;
18
16
use rustc_middle:: hir:: nested_filter;
19
17
use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
20
18
use rustc_middle:: middle:: stability:: EvalResult ;
@@ -1510,58 +1508,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
1510
1508
return ;
1511
1509
}
1512
1510
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).
1515
1512
let field_infos = adt. all_fields ( ) . map ( |field| {
1516
1513
let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
1517
1514
let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, field. did ) ;
1518
1515
let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
1519
1516
// We are currently checking the type this field came from, so it must be local
1520
1517
let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
1521
1518
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)
1565
1520
} ) ;
1566
1521
1567
1522
let non_trivial_fields = field_infos
@@ -1578,36 +1533,84 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
1578
1533
) ;
1579
1534
return ;
1580
1535
}
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
+ }
1544
+ struct UnsuitedInfo < ' tcx > {
1545
+ descr : & ' static str ,
1546
+ def_id : DefId ,
1547
+ args : GenericArgsRef < ' tcx > ,
1548
+ reason : UnsuitedReason ,
1549
+ }
1550
+
1551
+ fn check_unsuited_1zst < ' tcx > (
1552
+ tcx : TyCtxt < ' tcx > ,
1553
+ t : Ty < ' tcx > ,
1554
+ ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
1555
+ match t. kind ( ) {
1556
+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited_1zst ( tcx, t) ) ,
1557
+ ty:: Array ( ty, _) => check_unsuited_1zst ( tcx, * ty) ,
1558
+ ty:: Adt ( def, args) => {
1559
+ if !def. did ( ) . is_local ( )
1560
+ && !find_attr ! ( tcx. get_all_attrs( def. did( ) ) , AttributeKind :: PubTransparent ( _) )
1561
+ {
1562
+ let non_exhaustive = def. is_variant_list_non_exhaustive ( )
1563
+ || def. variants ( ) . iter ( ) . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
1564
+ let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
1565
+ if non_exhaustive || has_priv {
1566
+ return ControlFlow :: Break ( UnsuitedInfo {
1567
+ descr : def. descr ( ) ,
1568
+ def_id : def. did ( ) ,
1569
+ args,
1570
+ reason : if non_exhaustive {
1571
+ UnsuitedReason :: NonExhaustive
1572
+ } else {
1573
+ UnsuitedReason :: PrivateField
1574
+ } ,
1575
+ } ) ;
1576
+ }
1577
+ }
1578
+ def. all_fields ( )
1579
+ . map ( |field| field. ty ( tcx, args) )
1580
+ . try_for_each ( |t| check_unsuited_1zst ( tcx, t) )
1581
+ }
1582
+ _ => ControlFlow :: Continue ( ( ) ) ,
1583
+ }
1584
+ }
1585
+
1586
+ let mut prev_unsuited_1zst = false ;
1587
+ for ( span, trivial, ty) in field_infos {
1588
+ if !trivial {
1589
+ continue ;
1590
+ }
1591
+ if let Some ( unsuited) = check_unsuited_1zst ( tcx, ty) . break_value ( ) {
1584
1592
// If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
1585
1593
// 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 ( ) ) ,
1594
+ if non_trivial_count > 0 || prev_unsuited_1zst {
1595
+ let mut diag = tcx. dcx ( ) . struct_span_err (
1590
1596
span,
1591
- |lint| {
1592
- lint. primary_message (
1593
- "zero-sized fields in `repr(transparent)` cannot \
1597
+ "zero-sized fields in `repr(transparent)` cannot \
1594
1598
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}, \
1599
+ ) ;
1600
+ let note = match unsuited. reason {
1601
+ UnsuitedReason :: NonExhaustive => "is marked with `#[non_exhaustive]`" ,
1602
+ UnsuitedReason :: PrivateField => "contains private fields" ,
1603
+ } ;
1604
+ let field_ty = tcx. def_path_str_with_args ( unsuited. def_id , unsuited. args ) ;
1605
+ diag. note ( format ! (
1606
+ "this {descr} contains `{field_ty}`, which {note}, \
1604
1607
and makes it not a breaking change to become \
1605
- non-zero-sized in the future."
1606
- ) ) ;
1607
- } ,
1608
- )
1608
+ non-zero-sized in the future.",
1609
+ descr = unsuited . descr ,
1610
+ ) ) ;
1611
+ diag . emit ( ) ;
1609
1612
} else {
1610
- prev_non_exhaustive_1zst = true ;
1613
+ prev_unsuited_1zst = true ;
1611
1614
}
1612
1615
}
1613
1616
}
0 commit comments