1
+ use crate :: build:: ExprCategory ;
1
2
use crate :: thir:: visit:: { self , Visitor } ;
2
3
3
4
use rustc_errors:: struct_span_err;
4
5
use rustc_hir as hir;
6
+ use rustc_middle:: mir:: BorrowKind ;
5
7
use rustc_middle:: thir:: * ;
6
- use rustc_middle:: ty:: { self , TyCtxt } ;
8
+ use rustc_middle:: ty:: { self , ParamEnv , TyCtxt } ;
7
9
use rustc_session:: lint:: builtin:: { UNSAFE_OP_IN_UNSAFE_FN , UNUSED_UNSAFE } ;
8
10
use rustc_session:: lint:: Level ;
9
11
use rustc_span:: def_id:: { DefId , LocalDefId } ;
@@ -27,6 +29,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
27
29
body_target_features : & ' tcx Vec < Symbol > ,
28
30
in_possible_lhs_union_assign : bool ,
29
31
in_union_destructure : bool ,
32
+ param_env : ParamEnv < ' tcx > ,
33
+ inside_adt : bool ,
30
34
}
31
35
32
36
impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
@@ -133,6 +137,50 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
133
137
}
134
138
}
135
139
140
+ // Searches for accesses to layout constrained fields.
141
+ struct LayoutConstrainedPlaceVisitor < ' a , ' tcx > {
142
+ found : bool ,
143
+ thir : & ' a Thir < ' tcx > ,
144
+ tcx : TyCtxt < ' tcx > ,
145
+ }
146
+
147
+ impl < ' a , ' tcx > LayoutConstrainedPlaceVisitor < ' a , ' tcx > {
148
+ fn new ( thir : & ' a Thir < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
149
+ Self { found : false , thir, tcx }
150
+ }
151
+ }
152
+
153
+ impl < ' a , ' tcx > Visitor < ' a , ' tcx > for LayoutConstrainedPlaceVisitor < ' a , ' tcx > {
154
+ fn thir ( & self ) -> & ' a Thir < ' tcx > {
155
+ self . thir
156
+ }
157
+
158
+ fn visit_expr ( & mut self , expr : & Expr < ' tcx > ) {
159
+ match expr. kind {
160
+ ExprKind :: Field { lhs, .. } => {
161
+ if let ty:: Adt ( adt_def, _) = self . thir [ lhs] . ty . kind ( ) {
162
+ if ( Bound :: Unbounded , Bound :: Unbounded )
163
+ != self . tcx . layout_scalar_valid_range ( adt_def. did )
164
+ {
165
+ self . found = true ;
166
+ }
167
+ }
168
+ visit:: walk_expr ( self , expr) ;
169
+ }
170
+
171
+ // Keep walking through the expression as long as we stay in the same
172
+ // place, i.e. the expression is a place expression and not a dereference
173
+ // (since dereferencing something leads us to a different place).
174
+ ExprKind :: Deref { .. } => { }
175
+ ref kind if ExprCategory :: of ( kind) . map_or ( true , |cat| cat == ExprCategory :: Place ) => {
176
+ visit:: walk_expr ( self , expr) ;
177
+ }
178
+
179
+ _ => { }
180
+ }
181
+ }
182
+ }
183
+
136
184
impl < ' a , ' tcx > Visitor < ' a , ' tcx > for UnsafetyVisitor < ' a , ' tcx > {
137
185
fn thir ( & self ) -> & ' a Thir < ' tcx > {
138
186
& self . thir
@@ -160,60 +208,82 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
160
208
}
161
209
162
210
fn visit_pat ( & mut self , pat : & Pat < ' tcx > ) {
163
- use PatKind :: * ;
164
-
165
211
if self . in_union_destructure {
166
212
match * pat. kind {
167
213
// binding to a variable allows getting stuff out of variable
168
- Binding { .. }
214
+ PatKind :: Binding { .. }
169
215
// match is conditional on having this value
170
- | Constant { .. }
171
- | Variant { .. }
172
- | Leaf { .. }
173
- | Deref { .. }
174
- | Range { .. }
175
- | Slice { .. }
176
- | Array { .. } => {
216
+ | PatKind :: Constant { .. }
217
+ | PatKind :: Variant { .. }
218
+ | PatKind :: Leaf { .. }
219
+ | PatKind :: Deref { .. }
220
+ | PatKind :: Range { .. }
221
+ | PatKind :: Slice { .. }
222
+ | PatKind :: Array { .. } => {
177
223
self . requires_unsafe ( pat. span , AccessToUnionField ) ;
178
- return ; // don't walk pattern
224
+ return ; // we can return here since this already requires unsafe
179
225
}
180
226
// wildcard doesn't take anything
181
- Wild |
227
+ PatKind :: Wild |
182
228
// these just wrap other patterns
183
- Or { .. } |
184
- AscribeUserType { .. } => { }
229
+ PatKind :: Or { .. } |
230
+ PatKind :: AscribeUserType { .. } => { }
185
231
}
186
232
} ;
187
233
188
- if let ty:: Adt ( adt_def, _) = pat. ty . kind ( ) {
189
- // check for extracting values from union via destructuring
190
- if adt_def. is_union ( ) {
191
- match * pat. kind {
192
- // assigning the whole union is okay
193
- // let x = Union { ... };
194
- // let y = x; // safe
195
- Binding { .. } |
196
- // binding to wildcard is okay since that never reads anything and stops double errors
197
- // with implict wildcard branches from `if let`s
198
- Wild |
199
- // doesn't have any effect on semantics
200
- AscribeUserType { .. } |
201
- // creating a union literal
202
- Constant { .. } => { } ,
203
- Leaf { .. } | Or { .. } => {
204
- // pattern matching with a union and not doing something like v = Union { bar: 5 }
205
- self . in_union_destructure = true ;
234
+ match & * pat. kind {
235
+ PatKind :: Leaf { .. } => {
236
+ if let ty:: Adt ( adt_def, ..) = pat. ty . kind ( ) {
237
+ if adt_def. is_union ( ) {
238
+ let old_in_union_destructure =
239
+ std:: mem:: replace ( & mut self . in_union_destructure , true ) ;
240
+ visit:: walk_pat ( self , pat) ;
241
+ self . in_union_destructure = old_in_union_destructure;
242
+ } else if ( Bound :: Unbounded , Bound :: Unbounded )
243
+ != self . tcx . layout_scalar_valid_range ( adt_def. did )
244
+ {
245
+ let old_inside_adt = std:: mem:: replace ( & mut self . inside_adt , true ) ;
246
+ visit:: walk_pat ( self , pat) ;
247
+ self . inside_adt = old_inside_adt;
248
+ } else {
206
249
visit:: walk_pat ( self , pat) ;
207
- self . in_union_destructure = false ;
208
- return ; // don't walk pattern
209
250
}
210
- Variant { .. } | Deref { .. } | Range { .. } | Slice { .. } | Array { .. } =>
211
- unreachable ! ( "impossible union destructuring type" ) ,
251
+ } else {
252
+ visit :: walk_pat ( self , pat ) ;
212
253
}
213
254
}
255
+ PatKind :: Binding { mode : BindingMode :: ByRef ( borrow_kind) , ty, .. } => {
256
+ if self . inside_adt {
257
+ if let ty:: Ref ( _, ty, _) = ty. kind ( ) {
258
+ match borrow_kind {
259
+ BorrowKind :: Shallow | BorrowKind :: Shared | BorrowKind :: Unique => {
260
+ if !ty. is_freeze ( self . tcx . at ( pat. span ) , self . param_env ) {
261
+ self . requires_unsafe ( pat. span , BorrowOfLayoutConstrainedField ) ;
262
+ }
263
+ }
264
+ BorrowKind :: Mut { .. } => {
265
+ self . requires_unsafe ( pat. span , MutationOfLayoutConstrainedField ) ;
266
+ }
267
+ }
268
+ } else {
269
+ span_bug ! (
270
+ pat. span,
271
+ "BindingMode::ByRef in pattern, but found non-reference type {}" ,
272
+ ty
273
+ ) ;
274
+ }
275
+ }
276
+ visit:: walk_pat ( self , pat) ;
277
+ }
278
+ PatKind :: Deref { .. } => {
279
+ let old_inside_adt = std:: mem:: replace ( & mut self . inside_adt , false ) ;
280
+ visit:: walk_pat ( self , pat) ;
281
+ self . inside_adt = old_inside_adt;
282
+ }
283
+ _ => {
284
+ visit:: walk_pat ( self , pat) ;
285
+ }
214
286
}
215
-
216
- visit:: walk_pat ( self , pat) ;
217
287
}
218
288
219
289
fn visit_expr ( & mut self , expr : & Expr < ' tcx > ) {
@@ -350,15 +420,46 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
350
420
}
351
421
}
352
422
}
353
- // don't have any special handling for AssignOp since it causes a read *and* write to lhs
354
- ExprKind :: Assign { lhs, rhs } => {
355
- // assigning to a union is safe, check here so it doesn't get treated as a read later
356
- self . in_possible_lhs_union_assign = true ;
357
- visit:: walk_expr ( self , & self . thir ( ) [ lhs] ) ;
358
- self . in_possible_lhs_union_assign = false ;
359
- visit:: walk_expr ( self , & self . thir ( ) [ rhs] ) ;
360
- return ; // don't visit the whole expression
423
+ ExprKind :: Assign { lhs, rhs } | ExprKind :: AssignOp { lhs, rhs, .. } => {
424
+ // First, check whether we are mutating a layout constrained field
425
+ let mut visitor = LayoutConstrainedPlaceVisitor :: new ( self . thir , self . tcx ) ;
426
+ visit:: walk_expr ( & mut visitor, & self . thir [ lhs] ) ;
427
+ if visitor. found {
428
+ self . requires_unsafe ( expr. span , MutationOfLayoutConstrainedField ) ;
429
+ }
430
+
431
+ // Second, check for accesses to union fields
432
+ // don't have any special handling for AssignOp since it causes a read *and* write to lhs
433
+ if matches ! ( expr. kind, ExprKind :: Assign { .. } ) {
434
+ // assigning to a union is safe, check here so it doesn't get treated as a read later
435
+ self . in_possible_lhs_union_assign = true ;
436
+ visit:: walk_expr ( self , & self . thir ( ) [ lhs] ) ;
437
+ self . in_possible_lhs_union_assign = false ;
438
+ visit:: walk_expr ( self , & self . thir ( ) [ rhs] ) ;
439
+ return ; // we have already visited everything by now
440
+ }
361
441
}
442
+ ExprKind :: Borrow { borrow_kind, arg } => match borrow_kind {
443
+ BorrowKind :: Shallow | BorrowKind :: Shared | BorrowKind :: Unique => {
444
+ if !self . thir [ arg]
445
+ . ty
446
+ . is_freeze ( self . tcx . at ( self . thir [ arg] . span ) , self . param_env )
447
+ {
448
+ let mut visitor = LayoutConstrainedPlaceVisitor :: new ( self . thir , self . tcx ) ;
449
+ visit:: walk_expr ( & mut visitor, expr) ;
450
+ if visitor. found {
451
+ self . requires_unsafe ( expr. span , BorrowOfLayoutConstrainedField ) ;
452
+ }
453
+ }
454
+ }
455
+ BorrowKind :: Mut { .. } => {
456
+ let mut visitor = LayoutConstrainedPlaceVisitor :: new ( self . thir , self . tcx ) ;
457
+ visit:: walk_expr ( & mut visitor, expr) ;
458
+ if visitor. found {
459
+ self . requires_unsafe ( expr. span , MutationOfLayoutConstrainedField ) ;
460
+ }
461
+ }
462
+ } ,
362
463
_ => { }
363
464
}
364
465
visit:: walk_expr ( self , expr) ;
@@ -520,6 +621,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
520
621
body_target_features,
521
622
in_possible_lhs_union_assign : false ,
522
623
in_union_destructure : false ,
624
+ param_env : tcx. param_env ( def. did ) ,
625
+ inside_adt : false ,
523
626
} ;
524
627
visitor. visit_expr ( & thir[ expr] ) ;
525
628
}
0 commit comments