Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 240ff4c

Browse files
committedJul 9, 2021
Auto merge of #85263 - Smittyvb:thir-unsafeck-union-field, r=oli-obk
Check for union field accesses in THIR unsafeck see also #85259, #83129, rust-lang/project-thir-unsafeck#7 r? `@LeSeulArtichaut`
2 parents 3eff244 + b86ed4a commit 240ff4c

File tree

72 files changed

+1095
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1095
-76
lines changed
 

‎compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

3133
impl<'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
}

‎compiler/rustc_mir_build/src/thir/visit.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
153153
}
154154

155155
pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stmt<'tcx>) {
156-
match stmt.kind {
157-
StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[expr]),
156+
match &stmt.kind {
157+
StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
158158
StmtKind::Let {
159159
initializer,
160160
remainder_scope: _,
@@ -163,7 +163,7 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
163163
lint_level: _,
164164
} => {
165165
if let Some(init) = initializer {
166-
visitor.visit_expr(&visitor.thir()[init]);
166+
visitor.visit_expr(&visitor.thir()[*init]);
167167
}
168168
visitor.visit_pat(pattern);
169169
}

0 commit comments

Comments
 (0)
Please sign in to comment.