@@ -181,6 +181,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
181181 self . check_mut_borrowing_layout_constrained_field ( * place, context. is_mutating_use ( ) ) ;
182182 }
183183
184+ // Check for borrows to packed fields.
185+ // `is_disaligned` already traverses the place to consider all projections after the last
186+ // `Deref`, so this only needs to be called once at the top level.
184187 if context. is_borrow ( ) {
185188 if util:: is_disaligned ( self . tcx , self . body , self . param_env , * place) {
186189 self . require_unsafe (
@@ -190,87 +193,105 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
190193 }
191194 }
192195
193- for ( i, elem) in place. projection . iter ( ) . enumerate ( ) {
194- let proj_base = & place. projection [ ..i] ;
195- if context. is_borrow ( ) {
196- if util:: is_disaligned ( self . tcx , self . body , self . param_env , * place) {
196+ // Some checks below need the extra metainfo of the local declaration.
197+ let decl = & self . body . local_decls [ place. local ] ;
198+
199+ // Check the base local: it might be an unsafe-to-access static. We only check derefs of the
200+ // temporary holding the static pointer to avoid duplicate errors
201+ // <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
202+ if decl. internal && place. projection . first ( ) == Some ( & ProjectionElem :: Deref ) {
203+ // If the projection root is an artifical local that we introduced when
204+ // desugaring `static`, give a more specific error message
205+ // (avoid the general "raw pointer" clause below, that would only be confusing).
206+ if let Some ( box LocalInfo :: StaticRef { def_id, .. } ) = decl. local_info {
207+ if self . tcx . is_mutable_static ( def_id) {
197208 self . require_unsafe (
198- UnsafetyViolationKind :: BorrowPacked ,
199- UnsafetyViolationDetails :: BorrowOfPackedField ,
209+ UnsafetyViolationKind :: General ,
210+ UnsafetyViolationDetails :: UseOfMutableStatic ,
200211 ) ;
212+ return ;
213+ } else if self . tcx . is_foreign_item ( def_id) {
214+ self . require_unsafe (
215+ UnsafetyViolationKind :: General ,
216+ UnsafetyViolationDetails :: UseOfExternStatic ,
217+ ) ;
218+ return ;
201219 }
202220 }
203- let source_info = self . source_info ;
204- if let [ ] = proj_base {
205- let decl = & self . body . local_decls [ place. local ] ;
206- if decl. internal {
207- // If the projection root is an artifical local that we introduced when
208- // desugaring `static`, give a more specific error message
209- // (avoid the general "raw pointer" clause below, that would only be confusing).
210- if let Some ( box LocalInfo :: StaticRef { def_id, .. } ) = decl. local_info {
211- if self . tcx . is_mutable_static ( def_id) {
212- self . require_unsafe (
213- UnsafetyViolationKind :: General ,
214- UnsafetyViolationDetails :: UseOfMutableStatic ,
215- ) ;
216- return ;
217- } else if self . tcx . is_foreign_item ( def_id) {
218- self . require_unsafe (
219- UnsafetyViolationKind :: General ,
220- UnsafetyViolationDetails :: UseOfExternStatic ,
221- ) ;
222- return ;
223- }
224- } else {
225- // Internal locals are used in the `move_val_init` desugaring.
226- // We want to check unsafety against the source info of the
227- // desugaring, rather than the source info of the RHS.
228- self . source_info = self . body . local_decls [ place. local ] . source_info ;
229- }
221+ }
222+
223+ // Check for raw pointer `Deref`.
224+ for ( base, proj) in place. iter_projections ( ) {
225+ if proj == ProjectionElem :: Deref {
226+ let source_info = self . source_info ; // Backup source_info so we can restore it later.
227+ if base. projection . is_empty ( ) && decl. internal {
228+ // Internal locals are used in the `move_val_init` desugaring.
229+ // We want to check unsafety against the source info of the
230+ // desugaring, rather than the source info of the RHS.
231+ self . source_info = self . body . local_decls [ place. local ] . source_info ;
232+ }
233+ let base_ty = base. ty ( self . body , self . tcx ) . ty ;
234+ if base_ty. is_unsafe_ptr ( ) {
235+ self . require_unsafe (
236+ UnsafetyViolationKind :: GeneralAndConstFn ,
237+ UnsafetyViolationDetails :: DerefOfRawPointer ,
238+ )
230239 }
240+ self . source_info = source_info; // Restore backed-up source_info.
231241 }
232- let base_ty = Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
233- match base_ty. kind ( ) {
234- ty:: RawPtr ( ..) => self . require_unsafe (
235- UnsafetyViolationKind :: GeneralAndConstFn ,
236- UnsafetyViolationDetails :: DerefOfRawPointer ,
237- ) ,
238- ty:: Adt ( adt, _) => {
239- if adt. is_union ( ) {
240- if context == PlaceContext :: MutatingUse ( MutatingUseContext :: Store )
241- || context == PlaceContext :: MutatingUse ( MutatingUseContext :: Drop )
242- || context == PlaceContext :: MutatingUse ( MutatingUseContext :: AsmOutput )
243- {
244- let elem_ty = match elem {
245- ProjectionElem :: Field ( _, ty) => ty,
246- _ => span_bug ! (
247- self . source_info. span,
248- "non-field projection {:?} from union?" ,
249- place
250- ) ,
251- } ;
252- if !elem_ty. is_copy_modulo_regions (
253- self . tcx . at ( self . source_info . span ) ,
254- self . param_env ,
255- ) {
256- self . require_unsafe (
257- UnsafetyViolationKind :: GeneralAndConstFn ,
258- UnsafetyViolationDetails :: AssignToNonCopyUnionField ,
259- )
260- } else {
261- // write to non-move union, safe
262- }
263- } else {
264- self . require_unsafe (
265- UnsafetyViolationKind :: GeneralAndConstFn ,
266- UnsafetyViolationDetails :: AccessToUnionField ,
267- )
268- }
242+ }
243+
244+ // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
245+ // whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
246+ let mut saw_deref = false ;
247+ for ( base, proj) in place. iter_projections ( ) . rev ( ) {
248+ if proj == ProjectionElem :: Deref {
249+ saw_deref = true ;
250+ continue ;
251+ }
252+
253+ let base_ty = base. ty ( self . body , self . tcx ) . ty ;
254+ if base_ty. ty_adt_def ( ) . map_or ( false , |adt| adt. is_union ( ) ) {
255+ // If we did not hit a `Deref` yet and the overall place use is an assignment, the
256+ // rules are different.
257+ let assign_to_field = !saw_deref
258+ && matches ! (
259+ context,
260+ PlaceContext :: MutatingUse (
261+ MutatingUseContext :: Store
262+ | MutatingUseContext :: Drop
263+ | MutatingUseContext :: AsmOutput
264+ )
265+ ) ;
266+ // If this is just an assignment, determine if the assigned type needs dropping.
267+ if assign_to_field {
268+ // We have to check the actual type of the assignment, as that determines if the
269+ // old value is being dropped.
270+ let assigned_ty = place. ty ( & self . body . local_decls , self . tcx ) . ty ;
271+ // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
272+ let manually_drop = assigned_ty
273+ . ty_adt_def ( )
274+ . map_or ( false , |adt_def| adt_def. is_manually_drop ( ) ) ;
275+ let nodrop = manually_drop
276+ || assigned_ty. is_copy_modulo_regions (
277+ self . tcx . at ( self . source_info . span ) ,
278+ self . param_env ,
279+ ) ;
280+ if !nodrop {
281+ self . require_unsafe (
282+ UnsafetyViolationKind :: GeneralAndConstFn ,
283+ UnsafetyViolationDetails :: AssignToDroppingUnionField ,
284+ ) ;
285+ } else {
286+ // write to non-drop union field, safe
269287 }
288+ } else {
289+ self . require_unsafe (
290+ UnsafetyViolationKind :: GeneralAndConstFn ,
291+ UnsafetyViolationDetails :: AccessToUnionField ,
292+ )
270293 }
271- _ => { }
272294 }
273- self . source_info = source_info;
274295 }
275296 }
276297}
0 commit comments