@@ -26,6 +26,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
26
26
/// calls to functions with `#[target_feature]` (RFC 2396).
27
27
body_target_features : & ' tcx Vec < Symbol > ,
28
28
is_const : bool ,
29
+ in_possible_lhs_union_assign : bool ,
30
+ in_union_destructure : bool ,
29
31
}
30
32
31
33
impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
@@ -158,14 +160,115 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
158
160
}
159
161
}
160
162
163
+ fn visit_pat ( & mut self , pat : & Pat < ' tcx > ) {
164
+ use PatKind :: * ;
165
+
166
+ if self . in_union_destructure {
167
+ match * pat. kind {
168
+ // binding to a variable allows getting stuff out of variable
169
+ Binding { .. }
170
+ // match is conditional on having this value
171
+ | Constant { .. }
172
+ | Variant { .. }
173
+ | Leaf { .. }
174
+ | Deref { .. }
175
+ | Range { .. }
176
+ | Slice { .. }
177
+ | Array { .. } => {
178
+ self . requires_unsafe ( pat. span , AccessToUnionField ) ;
179
+ return ; // don't walk pattern
180
+ }
181
+ // wildcard doesn't take anything
182
+ Wild |
183
+ // these just wrap other patterns
184
+ Or { .. } |
185
+ AscribeUserType { .. } => { }
186
+ }
187
+ } ;
188
+
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
+ Variant { .. } | Leaf { .. } | Or { .. } => {
205
+ // pattern matching with a union and not doing something like v = Union { bar: 5 }
206
+ self . in_union_destructure = true ;
207
+ visit:: walk_pat ( self , pat) ;
208
+ self . in_union_destructure = false ;
209
+ return ; // don't walk pattern
210
+ }
211
+ Deref { .. } | Range { .. } | Slice { .. } | Array { .. } =>
212
+ unreachable ! ( "impossible union destructuring type" ) ,
213
+ }
214
+ }
215
+ }
216
+
217
+ visit:: walk_pat ( self , pat) ;
218
+ }
219
+
161
220
fn visit_expr ( & mut self , expr : & Expr < ' tcx > ) {
221
+ // could we be in a the LHS of an assignment of a union?
222
+ match expr. kind {
223
+ ExprKind :: Field { .. }
224
+ | ExprKind :: VarRef { .. }
225
+ | ExprKind :: UpvarRef { .. }
226
+ | ExprKind :: Scope { .. }
227
+ | ExprKind :: Cast { .. } => { }
228
+
229
+ ExprKind :: AddressOf { .. }
230
+ | ExprKind :: Adt { .. }
231
+ | ExprKind :: Array { .. }
232
+ | ExprKind :: Binary { .. }
233
+ | ExprKind :: Block { .. }
234
+ | ExprKind :: Borrow { .. }
235
+ | ExprKind :: Literal { .. }
236
+ | ExprKind :: ConstBlock { .. }
237
+ | ExprKind :: Deref { .. }
238
+ | ExprKind :: Index { .. }
239
+ | ExprKind :: NeverToAny { .. }
240
+ | ExprKind :: PlaceTypeAscription { .. }
241
+ | ExprKind :: ValueTypeAscription { .. }
242
+ | ExprKind :: Pointer { .. }
243
+ | ExprKind :: Repeat { .. }
244
+ | ExprKind :: StaticRef { .. }
245
+ | ExprKind :: ThreadLocalRef { .. }
246
+ | ExprKind :: Tuple { .. }
247
+ | ExprKind :: Unary { .. }
248
+ | ExprKind :: Call { .. }
249
+ | ExprKind :: Assign { .. }
250
+ | ExprKind :: AssignOp { .. }
251
+ | ExprKind :: Break { .. }
252
+ | ExprKind :: Closure { .. }
253
+ | ExprKind :: Continue { .. }
254
+ | ExprKind :: Return { .. }
255
+ | ExprKind :: Yield { .. }
256
+ | ExprKind :: Loop { .. }
257
+ | ExprKind :: Match { .. }
258
+ | ExprKind :: Box { .. }
259
+ | ExprKind :: If { .. }
260
+ | ExprKind :: InlineAsm { .. }
261
+ | ExprKind :: LlvmInlineAsm { .. }
262
+ | ExprKind :: LogicalOp { .. }
263
+ | ExprKind :: Use { .. } => self . in_possible_lhs_union_assign = false ,
264
+ } ;
162
265
match expr. kind {
163
266
ExprKind :: Scope { value, lint_level : LintLevel :: Explicit ( hir_id) , region_scope : _ } => {
164
267
let prev_id = self . hir_context ;
165
268
self . hir_context = hir_id;
166
269
self . visit_expr ( & self . thir [ value] ) ;
167
270
self . hir_context = prev_id;
168
- return ;
271
+ return ; // don't visit the whole expression
169
272
}
170
273
ExprKind :: Call { fun, ty : _, args : _, from_hir_call : _, fn_span : _ } => {
171
274
if self . thir [ fun] . ty . fn_sig ( self . tcx ) . unsafety ( ) == hir:: Unsafety :: Unsafe {
@@ -246,9 +349,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
246
349
// Unsafe blocks can be used in closures, make sure to take it into account
247
350
self . safety_context = closure_visitor. safety_context ;
248
351
}
352
+ ExprKind :: Field { lhs, .. } => {
353
+ // assigning to union field is okay for AccessToUnionField
354
+ if let ty:: Adt ( adt_def, _) = & self . thir [ lhs] . ty . kind ( ) {
355
+ if adt_def. is_union ( ) {
356
+ if self . in_possible_lhs_union_assign {
357
+ // FIXME: trigger AssignToDroppingUnionField unsafety if needed
358
+ } else {
359
+ self . requires_unsafe ( expr. span , AccessToUnionField ) ;
360
+ }
361
+ }
362
+ }
363
+ }
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
372
+ }
249
373
_ => { }
250
374
}
251
-
252
375
visit:: walk_expr ( self , expr) ;
253
376
}
254
377
}
@@ -296,7 +419,6 @@ enum UnsafeOpKind {
296
419
DerefOfRawPointer ,
297
420
#[ allow( dead_code) ] // FIXME
298
421
AssignToDroppingUnionField ,
299
- #[ allow( dead_code) ] // FIXME
300
422
AccessToUnionField ,
301
423
#[ allow( dead_code) ] // FIXME
302
424
MutationOfLayoutConstrainedField ,
@@ -417,6 +539,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
417
539
body_unsafety,
418
540
body_target_features,
419
541
is_const,
542
+ in_possible_lhs_union_assign : false ,
543
+ in_union_destructure : false ,
420
544
} ;
421
545
visitor. visit_expr ( & thir[ expr] ) ;
422
546
}
0 commit comments