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