@@ -26,6 +26,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
2626 /// calls to functions with `#[target_feature]` (RFC 2396).
2727 body_target_features : & ' tcx Vec < Symbol > ,
2828 is_const : bool ,
29+ in_possible_lhs_union_assign : bool ,
30+ in_union_destructure : bool ,
2931}
3032
3133impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
@@ -158,14 +160,115 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
158160 }
159161 }
160162
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+ 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+ Variant { .. } | Deref { .. } | Range { .. } | Slice { .. } | Array { .. } =>
212+ unreachable ! ( "impossible union destructuring type" ) ,
213+ }
214+ }
215+ }
216+
217+ visit:: walk_pat ( self , pat) ;
218+ }
219+
161220 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+ } ;
162265 match expr. kind {
163266 ExprKind :: Scope { value, lint_level : LintLevel :: Explicit ( hir_id) , region_scope : _ } => {
164267 let prev_id = self . hir_context ;
165268 self . hir_context = hir_id;
166269 self . visit_expr ( & self . thir [ value] ) ;
167270 self . hir_context = prev_id;
168- return ;
271+ return ; // don't visit the whole expression
169272 }
170273 ExprKind :: Call { fun, ty : _, args : _, from_hir_call : _, fn_span : _ } => {
171274 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> {
246349 // Unsafe blocks can be used in closures, make sure to take it into account
247350 self . safety_context = closure_visitor. safety_context ;
248351 }
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+ }
249373 _ => { }
250374 }
251-
252375 visit:: walk_expr ( self , expr) ;
253376 }
254377}
@@ -296,7 +419,6 @@ enum UnsafeOpKind {
296419 DerefOfRawPointer ,
297420 #[ allow( dead_code) ] // FIXME
298421 AssignToDroppingUnionField ,
299- #[ allow( dead_code) ] // FIXME
300422 AccessToUnionField ,
301423 #[ allow( dead_code) ] // FIXME
302424 MutationOfLayoutConstrainedField ,
@@ -417,6 +539,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
417539 body_unsafety,
418540 body_target_features,
419541 is_const,
542+ in_possible_lhs_union_assign : false ,
543+ in_union_destructure : false ,
420544 } ;
421545 visitor. visit_expr ( & thir[ expr] ) ;
422546}
0 commit comments