@@ -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:: {
@@ -71,6 +71,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
71
71
72
72
check_transparent ( tcx, def) ;
73
73
check_packed ( tcx, span, def) ;
74
+ check_unsafe_fields ( tcx, span, def_id) ;
74
75
}
75
76
76
77
fn check_union ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
@@ -82,38 +83,38 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
82
83
check_packed ( tcx, span, def) ;
83
84
}
84
85
86
+ fn allowed_union_or_unsafe_field < ' tcx > (
87
+ ty : Ty < ' tcx > ,
88
+ tcx : TyCtxt < ' tcx > ,
89
+ param_env : ty:: ParamEnv < ' tcx > ,
90
+ ) -> bool {
91
+ // We don't just accept all !needs_drop fields, due to semver concerns.
92
+ match ty. kind ( ) {
93
+ ty:: Ref ( ..) => true , // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
94
+ ty:: Tuple ( tys) => {
95
+ // allow tuples of allowed types
96
+ tys. iter ( ) . all ( |ty| allowed_union_or_unsafe_field ( ty, tcx, param_env) )
97
+ }
98
+ ty:: Array ( elem, _len) => {
99
+ // Like `Copy`, we do *not* special-case length 0.
100
+ allowed_union_or_unsafe_field ( * elem, tcx, param_env)
101
+ }
102
+ _ => {
103
+ // Fallback case: allow `ManuallyDrop` and things that are `Copy`,
104
+ // also no need to report an error if the type is unresolved.
105
+ ty. ty_adt_def ( ) . is_some_and ( |adt_def| adt_def. is_manually_drop ( ) )
106
+ || ty. is_copy_modulo_regions ( tcx, param_env)
107
+ || ty. references_error ( )
108
+ }
109
+ }
110
+ }
111
+
85
112
/// Check that the fields of the `union` do not need dropping.
86
113
fn check_union_fields ( tcx : TyCtxt < ' _ > , span : Span , item_def_id : LocalDefId ) -> bool {
87
114
let item_type = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
88
115
if let ty:: Adt ( def, args) = item_type. kind ( ) {
89
116
assert ! ( def. is_union( ) ) ;
90
117
91
- fn allowed_union_field < ' tcx > (
92
- ty : Ty < ' tcx > ,
93
- tcx : TyCtxt < ' tcx > ,
94
- param_env : ty:: ParamEnv < ' tcx > ,
95
- ) -> bool {
96
- // We don't just accept all !needs_drop fields, due to semver concerns.
97
- match ty. kind ( ) {
98
- ty:: Ref ( ..) => true , // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
99
- ty:: Tuple ( tys) => {
100
- // allow tuples of allowed types
101
- tys. iter ( ) . all ( |ty| allowed_union_field ( ty, tcx, param_env) )
102
- }
103
- ty:: Array ( elem, _len) => {
104
- // Like `Copy`, we do *not* special-case length 0.
105
- allowed_union_field ( * elem, tcx, param_env)
106
- }
107
- _ => {
108
- // Fallback case: allow `ManuallyDrop` and things that are `Copy`,
109
- // also no need to report an error if the type is unresolved.
110
- ty. ty_adt_def ( ) . is_some_and ( |adt_def| adt_def. is_manually_drop ( ) )
111
- || ty. is_copy_modulo_regions ( tcx, param_env)
112
- || ty. references_error ( )
113
- }
114
- }
115
- }
116
-
117
118
let param_env = tcx. param_env ( item_def_id) ;
118
119
for field in & def. non_enum_variant ( ) . fields {
119
120
let Ok ( field_ty) = tcx. try_normalize_erasing_regions ( param_env, field. ty ( tcx, args) )
@@ -122,7 +123,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
122
123
continue ;
123
124
} ;
124
125
125
- if !allowed_union_field ( field_ty, tcx, param_env) {
126
+ if !allowed_union_or_unsafe_field ( field_ty, tcx, param_env) {
126
127
let ( field_span, ty_span) = match tcx. hir ( ) . get_if_local ( field. did ) {
127
128
// We are currently checking the type this field came from, so it must be local.
128
129
Some ( Node :: Field ( field) ) => ( field. span , field. ty . span ) ,
@@ -149,6 +150,53 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
149
150
true
150
151
}
151
152
153
+ /// Check that the unsafe fields do not need dropping.
154
+ fn check_unsafe_fields ( tcx : TyCtxt < ' _ > , span : Span , item_def_id : LocalDefId ) -> bool {
155
+ let item_type = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
156
+ if let ty:: Adt ( def, args) = item_type. kind ( ) {
157
+ let param_env = tcx. param_env ( item_def_id) ;
158
+ for variant in def. variants ( ) {
159
+ for field in & variant. fields {
160
+ if field. safety != Safety :: Unsafe {
161
+ continue ;
162
+ }
163
+ let Ok ( field_ty) =
164
+ tcx. try_normalize_erasing_regions ( param_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, param_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
+ return false ;
185
+ } else if field_ty. needs_drop ( tcx, param_env) {
186
+ // This should never happen. But we can get here e.g. in case of name resolution errors.
187
+ tcx. dcx ( ) . span_delayed_bug (
188
+ span,
189
+ "we should never accept maybe-dropping union fields" ,
190
+ ) ;
191
+ }
192
+ }
193
+ }
194
+ } else {
195
+ span_bug ! ( span, "structs/enums must be ty::Adt, but got {:?}" , item_type. kind( ) ) ;
196
+ }
197
+ true
198
+ }
199
+
152
200
/// Check that a `static` is inhabited.
153
201
fn check_static_inhabited ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
154
202
// Make sure statics are inhabited.
@@ -1460,6 +1508,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
1460
1508
1461
1509
detect_discriminant_duplicate ( tcx, def) ;
1462
1510
check_transparent ( tcx, def) ;
1511
+ check_unsafe_fields ( tcx, tcx. def_span ( def_id) , def_id) ;
1463
1512
}
1464
1513
1465
1514
/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
0 commit comments