1+ use crate :: build:: ExprCategory ;
12use crate :: thir:: visit:: { self , Visitor } ;
23
34use rustc_errors:: struct_span_err;
45use rustc_hir as hir;
6+ use rustc_middle:: mir:: BorrowKind ;
57use rustc_middle:: thir:: * ;
6- use rustc_middle:: ty:: { self , TyCtxt } ;
8+ use rustc_middle:: ty:: { self , ParamEnv , TyCtxt } ;
79use rustc_session:: lint:: builtin:: { UNSAFE_OP_IN_UNSAFE_FN , UNUSED_UNSAFE } ;
810use rustc_session:: lint:: Level ;
911use rustc_span:: def_id:: { DefId , LocalDefId } ;
@@ -27,6 +29,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
2729 body_target_features : & ' tcx Vec < Symbol > ,
2830 in_possible_lhs_union_assign : bool ,
2931 in_union_destructure : bool ,
32+ param_env : ParamEnv < ' tcx > ,
33+ inside_adt : bool ,
3034}
3135
3236impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
@@ -133,6 +137,50 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
133137 }
134138}
135139
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+
136184impl < ' a , ' tcx > Visitor < ' a , ' tcx > for UnsafetyVisitor < ' a , ' tcx > {
137185 fn thir ( & self ) -> & ' a Thir < ' tcx > {
138186 & self . thir
@@ -160,60 +208,82 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
160208 }
161209
162210 fn visit_pat ( & mut self , pat : & Pat < ' tcx > ) {
163- use PatKind :: * ;
164-
165211 if self . in_union_destructure {
166212 match * pat. kind {
167213 // binding to a variable allows getting stuff out of variable
168- Binding { .. }
214+ PatKind :: Binding { .. }
169215 // 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 { .. } => {
177223 self . requires_unsafe ( pat. span , AccessToUnionField ) ;
178- return ; // don't walk pattern
224+ return ; // we can return here since this already requires unsafe
179225 }
180226 // wildcard doesn't take anything
181- Wild |
227+ PatKind :: Wild |
182228 // these just wrap other patterns
183- Or { .. } |
184- AscribeUserType { .. } => { }
229+ PatKind :: Or { .. } |
230+ PatKind :: AscribeUserType { .. } => { }
185231 }
186232 } ;
187233
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 {
206249 visit:: walk_pat ( self , pat) ;
207- self . in_union_destructure = false ;
208- return ; // don't walk pattern
209250 }
210- Variant { .. } | Deref { .. } | Range { .. } | Slice { .. } | Array { .. } =>
211- unreachable ! ( "impossible union destructuring type" ) ,
251+ } else {
252+ visit :: walk_pat ( self , pat ) ;
212253 }
213254 }
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+ }
214286 }
215-
216- visit:: walk_pat ( self , pat) ;
217287 }
218288
219289 fn visit_expr ( & mut self , expr : & Expr < ' tcx > ) {
@@ -350,15 +420,46 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
350420 }
351421 }
352422 }
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+ }
361441 }
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+ } ,
362463 _ => { }
363464 }
364465 visit:: walk_expr ( self , expr) ;
@@ -520,6 +621,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
520621 body_target_features,
521622 in_possible_lhs_union_assign : false ,
522623 in_union_destructure : false ,
624+ param_env : tcx. param_env ( def. did ) ,
625+ inside_adt : false ,
523626 } ;
524627 visitor. visit_expr ( & thir[ expr] ) ;
525628}
0 commit comments