Skip to content

Commit 1f0db5e

Browse files
committed
Auto merge of #86665 - FabianWolff:layout-field-thir-unsafeck, r=oli-obk
Implement Mutation- and BorrowOfLayoutConstrainedField in thir-unsafeck Since nobody has so far claimed Mutation- and BorrowOfLayoutConstrainedField in rust-lang/project-thir-unsafeck#7, I have taken the liberty of implementing them in thir-unsafeck. r? `@LeSeulArtichaut`
2 parents 14c0c3e + b088861 commit 1f0db5e

30 files changed

+512
-59
lines changed

compiler/rustc_mir_build/src/build/expr/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ mod as_operand;
6565
pub mod as_place;
6666
mod as_rvalue;
6767
mod as_temp;
68-
mod category;
68+
pub mod category;
6969
mod into;
7070
mod stmt;

compiler/rustc_mir_build/src/build/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1131,3 +1131,5 @@ mod expr;
11311131
mod matches;
11321132
mod misc;
11331133
mod scope;
1134+
1135+
pub(crate) use expr::category::Category as ExprCategory;

compiler/rustc_mir_build/src/check_unsafety.rs

+150-47
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use crate::build::ExprCategory;
12
use crate::thir::visit::{self, Visitor};
23

34
use rustc_errors::struct_span_err;
45
use rustc_hir as hir;
6+
use rustc_middle::mir::BorrowKind;
57
use rustc_middle::thir::*;
6-
use rustc_middle::ty::{self, TyCtxt};
8+
use rustc_middle::ty::{self, ParamEnv, TyCtxt};
79
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
810
use rustc_session::lint::Level;
911
use 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

3236
impl<'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+
136184
impl<'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
}

src/test/ui/unsafe/ranged_ints2.stderr renamed to src/test/ui/unsafe/ranged_ints2.mirunsafeck.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block
2-
--> $DIR/ranged_ints2.rs:8:13
2+
--> $DIR/ranged_ints2.rs:11:13
33
|
44
LL | let y = &mut x.0;
55
| ^^^^^^^^ mutation of layout constrained field

src/test/ui/unsafe/ranged_ints2.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// revisions: mirunsafeck thirunsafeck
2+
// [thirunsafeck]compile-flags: -Z thir-unsafeck
3+
14
#![feature(rustc_attrs)]
25

36
#[rustc_layout_scalar_valid_range_start(1)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block
2+
--> $DIR/ranged_ints2.rs:11:13
3+
|
4+
LL | let y = &mut x.0;
5+
| ^^^^^^^^ mutation of layout constrained field
6+
|
7+
= note: mutating layout constrained fields cannot statically be checked for valid values
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0133`.

src/test/ui/unsafe/ranged_ints2_const.stderr renamed to src/test/ui/unsafe/ranged_ints2_const.mirunsafeck.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0658]: mutable references are not allowed in constant functions
2-
--> $DIR/ranged_ints2_const.rs:11:13
2+
--> $DIR/ranged_ints2_const.rs:14:13
33
|
44
LL | let y = &mut x.0;
55
| ^^^^^^^^
@@ -8,7 +8,7 @@ LL | let y = &mut x.0;
88
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
99

1010
error[E0658]: mutable references are not allowed in constant functions
11-
--> $DIR/ranged_ints2_const.rs:18:22
11+
--> $DIR/ranged_ints2_const.rs:21:22
1212
|
1313
LL | let y = unsafe { &mut x.0 };
1414
| ^^^^^^^^
@@ -17,7 +17,7 @@ LL | let y = unsafe { &mut x.0 };
1717
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
1818

1919
error[E0658]: mutable references are not allowed in constant functions
20-
--> $DIR/ranged_ints2_const.rs:24:22
20+
--> $DIR/ranged_ints2_const.rs:27:22
2121
|
2222
LL | unsafe { let y = &mut x.0; }
2323
| ^^^^^^^^
@@ -26,7 +26,7 @@ LL | unsafe { let y = &mut x.0; }
2626
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
2727

2828
error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block
29-
--> $DIR/ranged_ints2_const.rs:11:13
29+
--> $DIR/ranged_ints2_const.rs:14:13
3030
|
3131
LL | let y = &mut x.0;
3232
| ^^^^^^^^ mutation of layout constrained field

src/test/ui/unsafe/ranged_ints2_const.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// revisions: mirunsafeck thirunsafeck
2+
// [thirunsafeck]compile-flags: -Z thir-unsafeck
3+
14
#![feature(rustc_attrs)]
25

36
#[rustc_layout_scalar_valid_range_start(1)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block
2+
--> $DIR/ranged_ints2_const.rs:14:13
3+
|
4+
LL | let y = &mut x.0;
5+
| ^^^^^^^^ mutation of layout constrained field
6+
|
7+
= note: mutating layout constrained fields cannot statically be checked for valid values
8+
9+
error[E0658]: mutable references are not allowed in constant functions
10+
--> $DIR/ranged_ints2_const.rs:14:13
11+
|
12+
LL | let y = &mut x.0;
13+
| ^^^^^^^^
14+
|
15+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
16+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
17+
18+
error[E0658]: mutable references are not allowed in constant functions
19+
--> $DIR/ranged_ints2_const.rs:21:22
20+
|
21+
LL | let y = unsafe { &mut x.0 };
22+
| ^^^^^^^^
23+
|
24+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
25+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
26+
27+
error[E0658]: mutable references are not allowed in constant functions
28+
--> $DIR/ranged_ints2_const.rs:27:22
29+
|
30+
LL | unsafe { let y = &mut x.0; }
31+
| ^^^^^^^^
32+
|
33+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
34+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
35+
36+
error: aborting due to 4 previous errors
37+
38+
Some errors have detailed explanations: E0133, E0658.
39+
For more information about an error, try `rustc --explain E0133`.

src/test/ui/unsafe/ranged_ints3.stderr renamed to src/test/ui/unsafe/ranged_ints3.mirunsafeck.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0133]: borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block
2-
--> $DIR/ranged_ints3.rs:10:13
2+
--> $DIR/ranged_ints3.rs:13:13
33
|
44
LL | let y = &x.0;
55
| ^^^^ borrow of layout constrained field with interior mutability

src/test/ui/unsafe/ranged_ints3.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// revisions: mirunsafeck thirunsafeck
2+
// [thirunsafeck]compile-flags: -Z thir-unsafeck
3+
14
#![feature(rustc_attrs)]
25

36
use std::cell::Cell;

0 commit comments

Comments
 (0)