@@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
6
6
use rustc_errors:: MultiSpan ;
7
7
use rustc_errors:: codes:: * ;
8
8
use rustc_hir:: def:: { CtorKind , DefKind } ;
9
- use rustc_hir:: { Node , intravisit} ;
9
+ use rustc_hir:: { Node , Safety , intravisit} ;
10
10
use rustc_infer:: infer:: { RegionVariableOrigin , TyCtxtInferExt } ;
11
11
use rustc_infer:: traits:: { Obligation , ObligationCauseCode } ;
12
12
use rustc_lint_defs:: builtin:: {
@@ -70,6 +70,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
70
70
71
71
check_transparent ( tcx, def) ;
72
72
check_packed ( tcx, span, def) ;
73
+ check_unsafe_fields ( tcx, span, def_id) ;
73
74
}
74
75
75
76
fn check_union ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
@@ -81,38 +82,38 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
81
82
check_packed ( tcx, span, def) ;
82
83
}
83
84
85
+ fn allowed_union_or_unsafe_field < ' tcx > (
86
+ ty : Ty < ' tcx > ,
87
+ tcx : TyCtxt < ' tcx > ,
88
+ typing_env : ty:: TypingEnv < ' tcx > ,
89
+ ) -> bool {
90
+ // We don't just accept all !needs_drop fields, due to semver concerns.
91
+ match ty. kind ( ) {
92
+ ty:: Ref ( ..) => true , // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
93
+ ty:: Tuple ( tys) => {
94
+ // allow tuples of allowed types
95
+ tys. iter ( ) . all ( |ty| allowed_union_or_unsafe_field ( ty, tcx, typing_env) )
96
+ }
97
+ ty:: Array ( elem, _len) => {
98
+ // Like `Copy`, we do *not* special-case length 0.
99
+ allowed_union_or_unsafe_field ( * elem, tcx, typing_env)
100
+ }
101
+ _ => {
102
+ // Fallback case: allow `ManuallyDrop` and things that are `Copy`,
103
+ // also no need to report an error if the type is unresolved.
104
+ ty. ty_adt_def ( ) . is_some_and ( |adt_def| adt_def. is_manually_drop ( ) )
105
+ || ty. is_copy_modulo_regions ( tcx, typing_env)
106
+ || ty. references_error ( )
107
+ }
108
+ }
109
+ }
110
+
84
111
/// Check that the fields of the `union` do not need dropping.
85
112
fn check_union_fields ( tcx : TyCtxt < ' _ > , span : Span , item_def_id : LocalDefId ) -> bool {
86
113
let item_type = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
87
114
if let ty:: Adt ( def, args) = item_type. kind ( ) {
88
115
assert ! ( def. is_union( ) ) ;
89
116
90
- fn allowed_union_field < ' tcx > (
91
- ty : Ty < ' tcx > ,
92
- tcx : TyCtxt < ' tcx > ,
93
- typing_env : ty:: TypingEnv < ' tcx > ,
94
- ) -> bool {
95
- // We don't just accept all !needs_drop fields, due to semver concerns.
96
- match ty. kind ( ) {
97
- ty:: Ref ( ..) => true , // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
98
- ty:: Tuple ( tys) => {
99
- // allow tuples of allowed types
100
- tys. iter ( ) . all ( |ty| allowed_union_field ( ty, tcx, typing_env) )
101
- }
102
- ty:: Array ( elem, _len) => {
103
- // Like `Copy`, we do *not* special-case length 0.
104
- allowed_union_field ( * elem, tcx, typing_env)
105
- }
106
- _ => {
107
- // Fallback case: allow `ManuallyDrop` and things that are `Copy`,
108
- // also no need to report an error if the type is unresolved.
109
- ty. ty_adt_def ( ) . is_some_and ( |adt_def| adt_def. is_manually_drop ( ) )
110
- || ty. is_copy_modulo_regions ( tcx, typing_env)
111
- || ty. references_error ( )
112
- }
113
- }
114
- }
115
-
116
117
let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, item_def_id) ;
117
118
for field in & def. non_enum_variant ( ) . fields {
118
119
let Ok ( field_ty) = tcx. try_normalize_erasing_regions ( typing_env, field. ty ( tcx, args) )
@@ -121,7 +122,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
121
122
continue ;
122
123
} ;
123
124
124
- if !allowed_union_field ( field_ty, tcx, typing_env) {
125
+ if !allowed_union_or_unsafe_field ( field_ty, tcx, typing_env) {
125
126
let ( field_span, ty_span) = match tcx. hir ( ) . get_if_local ( field. did ) {
126
127
// We are currently checking the type this field came from, so it must be local.
127
128
Some ( Node :: Field ( field) ) => ( field. span , field. ty . span ) ,
@@ -148,6 +149,47 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
148
149
true
149
150
}
150
151
152
+ /// Check that the unsafe fields do not need dropping.
153
+ fn check_unsafe_fields ( tcx : TyCtxt < ' _ > , span : Span , item_def_id : LocalDefId ) {
154
+ let item_type = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
155
+ let ty:: Adt ( def, args) = item_type. kind ( ) else {
156
+ span_bug ! ( span, "structs/enums must be ty::Adt, but got {:?}" , item_type. kind( ) ) ;
157
+ } ;
158
+ let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, item_def_id) ;
159
+ for variant in def. variants ( ) {
160
+ for field in & variant. fields {
161
+ if field. safety != Safety :: Unsafe {
162
+ continue ;
163
+ }
164
+ let Ok ( field_ty) = tcx. try_normalize_erasing_regions ( typing_env, field. ty ( tcx, args) )
165
+ else {
166
+ tcx. dcx ( ) . span_delayed_bug ( span, "could not normalize field type" ) ;
167
+ continue ;
168
+ } ;
169
+
170
+ if !allowed_union_or_unsafe_field ( field_ty, tcx, typing_env) {
171
+ let ( field_span, ty_span) = match tcx. hir ( ) . get_if_local ( field. did ) {
172
+ // We are currently checking the type this field came from, so it must be local.
173
+ Some ( Node :: Field ( field) ) => ( field. span , field. ty . span ) ,
174
+ _ => unreachable ! ( "mir field has to correspond to hir field" ) ,
175
+ } ;
176
+ tcx. dcx ( ) . emit_err ( errors:: InvalidUnsafeField {
177
+ field_span,
178
+ sugg : errors:: InvalidUnsafeFieldSuggestion {
179
+ lo : ty_span. shrink_to_lo ( ) ,
180
+ hi : ty_span. shrink_to_hi ( ) ,
181
+ } ,
182
+ note : ( ) ,
183
+ } ) ;
184
+ } else if field_ty. needs_drop ( tcx, typing_env) {
185
+ // This should never happen. But we can get here e.g. in case of name resolution errors.
186
+ tcx. dcx ( )
187
+ . span_delayed_bug ( span, "we should never accept maybe-dropping union fields" ) ;
188
+ }
189
+ }
190
+ }
191
+ }
192
+
151
193
/// Check that a `static` is inhabited.
152
194
fn check_static_inhabited ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
153
195
// Make sure statics are inhabited.
@@ -1462,6 +1504,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
1462
1504
1463
1505
detect_discriminant_duplicate ( tcx, def) ;
1464
1506
check_transparent ( tcx, def) ;
1507
+ check_unsafe_fields ( tcx, tcx. def_span ( def_id) , def_id) ;
1465
1508
}
1466
1509
1467
1510
/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
0 commit comments