@@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
66use rustc_errors:: MultiSpan ;
77use rustc_errors:: codes:: * ;
88use rustc_hir:: def:: { CtorKind , DefKind } ;
9- use rustc_hir:: { Node , intravisit} ;
9+ use rustc_hir:: { Node , Safety , intravisit} ;
1010use rustc_infer:: infer:: { RegionVariableOrigin , TyCtxtInferExt } ;
1111use rustc_infer:: traits:: { Obligation , ObligationCauseCode } ;
1212use rustc_lint_defs:: builtin:: {
@@ -70,6 +70,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
7070
7171 check_transparent ( tcx, def) ;
7272 check_packed ( tcx, span, def) ;
73+ check_unsafe_fields ( tcx, def_id) ;
7374}
7475
7576fn check_union ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
@@ -81,38 +82,45 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
8182 check_packed ( tcx, span, def) ;
8283}
8384
85+ fn allowed_union_or_unsafe_field < ' tcx > (
86+ tcx : TyCtxt < ' tcx > ,
87+ ty : Ty < ' tcx > ,
88+ typing_env : ty:: TypingEnv < ' tcx > ,
89+ span : Span ,
90+ ) -> bool {
91+ // We don't just accept all !needs_drop fields, due to semver concerns.
92+ let allowed = 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 ( tcx, ty, typing_env, span) )
97+ }
98+ ty:: Array ( elem, _len) => {
99+ // Like `Copy`, we do *not* special-case length 0.
100+ allowed_union_or_unsafe_field ( tcx, * elem, typing_env, span)
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, typing_env)
107+ || ty. references_error ( )
108+ }
109+ } ;
110+ if allowed && ty. needs_drop ( tcx, typing_env) {
111+ // This should never happen. But we can get here e.g. in case of name resolution errors.
112+ tcx. dcx ( )
113+ . span_delayed_bug ( span, "we should never accept maybe-dropping union or unsafe fields" ) ;
114+ }
115+ allowed
116+ }
117+
84118/// Check that the fields of the `union` do not need dropping.
85119fn check_union_fields ( tcx : TyCtxt < ' _ > , span : Span , item_def_id : LocalDefId ) -> bool {
86120 let item_type = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
87121 if let ty:: Adt ( def, args) = item_type. kind ( ) {
88122 assert ! ( def. is_union( ) ) ;
89123
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-
116124 let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, item_def_id) ;
117125 for field in & def. non_enum_variant ( ) . fields {
118126 let Ok ( field_ty) = tcx. try_normalize_erasing_regions ( typing_env, field. ty ( tcx, args) )
@@ -121,7 +129,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
121129 continue ;
122130 } ;
123131
124- if !allowed_union_field ( field_ty , tcx , typing_env) {
132+ if !allowed_union_or_unsafe_field ( tcx , field_ty , typing_env, span ) {
125133 let ( field_span, ty_span) = match tcx. hir ( ) . get_if_local ( field. did ) {
126134 // We are currently checking the type this field came from, so it must be local.
127135 Some ( Node :: Field ( field) ) => ( field. span , field. ty . span ) ,
@@ -136,10 +144,6 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
136144 note : ( ) ,
137145 } ) ;
138146 return false ;
139- } else if field_ty. needs_drop ( tcx, typing_env) {
140- // This should never happen. But we can get here e.g. in case of name resolution errors.
141- tcx. dcx ( )
142- . span_delayed_bug ( span, "we should never accept maybe-dropping union fields" ) ;
143147 }
144148 }
145149 } else {
@@ -148,6 +152,41 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
148152 true
149153}
150154
155+ /// Check that the unsafe fields do not need dropping.
156+ fn check_unsafe_fields ( tcx : TyCtxt < ' _ > , item_def_id : LocalDefId ) {
157+ let span = tcx. def_span ( item_def_id) ;
158+ let item_type = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
159+ let ty:: Adt ( def, args) = item_type. kind ( ) else {
160+ span_bug ! ( span, "structs/enums must be ty::Adt, but got {:?}" , item_type. kind( ) ) ;
161+ } ;
162+ let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, item_def_id) ;
163+ for field in def. all_fields ( ) {
164+ if field. safety != Safety :: Unsafe {
165+ continue ;
166+ }
167+ let Ok ( field_ty) = tcx. try_normalize_erasing_regions ( typing_env, field. ty ( tcx, args) )
168+ else {
169+ tcx. dcx ( ) . span_delayed_bug ( span, "could not normalize field type" ) ;
170+ continue ;
171+ } ;
172+
173+ if !allowed_union_or_unsafe_field ( tcx, field_ty, typing_env, span) {
174+ let hir:: Node :: Field ( field) = tcx. hir_node_by_def_id ( field. did . expect_local ( ) ) else {
175+ unreachable ! ( "field has to correspond to hir field" )
176+ } ;
177+ let ty_span = field. ty . span ;
178+ tcx. dcx ( ) . emit_err ( errors:: InvalidUnsafeField {
179+ field_span : field. span ,
180+ sugg : errors:: InvalidUnsafeFieldSuggestion {
181+ lo : ty_span. shrink_to_lo ( ) ,
182+ hi : ty_span. shrink_to_hi ( ) ,
183+ } ,
184+ note : ( ) ,
185+ } ) ;
186+ }
187+ }
188+ }
189+
151190/// Check that a `static` is inhabited.
152191fn check_static_inhabited ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
153192 // Make sure statics are inhabited.
@@ -1464,6 +1503,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
14641503
14651504 detect_discriminant_duplicate ( tcx, def) ;
14661505 check_transparent ( tcx, def) ;
1506+ check_unsafe_fields ( tcx, def_id) ;
14671507}
14681508
14691509/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
0 commit comments