Skip to content

Commit 584d823

Browse files
committed
Handle closures. Add some more tests.
1 parent e73d314 commit 584d823

File tree

4 files changed

+156
-14
lines changed

4 files changed

+156
-14
lines changed

src/librustc_mir/transform/add_validation.rs

+47-14
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
8787
use rustc::hir::intravisit::{self, Visitor};
8888
use rustc::hir::map::Node;
8989

90+
fn block_is_unsafe(block: &hir::Block) -> bool {
91+
use rustc::hir::BlockCheckMode::*;
92+
93+
match block.rules {
94+
UnsafeBlock(_) | PushUnsafeBlock(_) => true,
95+
// For PopUnsafeBlock, we don't actually know -- but we will always also check all
96+
// parent blocks, so we can safely declare the PopUnsafeBlock to not be unsafe.
97+
DefaultBlock | PopUnsafeBlock(_) => false,
98+
}
99+
}
100+
90101
let fn_node_id = match src {
91102
MirSource::Fn(node_id) => node_id,
92103
_ => return false, // only functions can have unsafe
@@ -101,8 +112,35 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
101112
match tcx.hir.find(fn_node_id) {
102113
Some(Node::NodeItem(item)) => finder.visit_item(item),
103114
Some(Node::NodeImplItem(item)) => finder.visit_impl_item(item),
115+
Some(Node::NodeExpr(item)) => {
116+
// This is a closure.
117+
// We also have to walk up the parents and check that there is no unsafe block
118+
// there.
119+
let mut cur = fn_node_id;
120+
loop {
121+
// Go further upwards.
122+
let parent = tcx.hir.get_parent_node(cur);
123+
if cur == parent {
124+
break;
125+
}
126+
cur = parent;
127+
// Check if this is a a block
128+
match tcx.hir.find(cur) {
129+
Some(Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..})) => {
130+
if block_is_unsafe(&*block) {
131+
// We can bail out here.
132+
return true;
133+
}
134+
}
135+
_ => {},
136+
}
137+
}
138+
// Finally, visit the closure itself.
139+
finder.visit_expr(item);
140+
}
104141
Some(_) | None =>
105-
bug!("Expected method or function, found {}", tcx.hir.node_to_string(fn_node_id)),
142+
bug!("Expected function, method or closure, found {}",
143+
tcx.hir.node_to_string(fn_node_id)),
106144
};
107145

108146
impl<'b, 'tcx> Visitor<'tcx> for FindUnsafe<'b, 'tcx> {
@@ -113,7 +151,7 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
113151
fn visit_fn(&mut self, fk: intravisit::FnKind<'tcx>, fd: &'tcx hir::FnDecl,
114152
b: hir::BodyId, s: Span, id: NodeId)
115153
{
116-
assert!(!self.found_unsafe, "We should never see more than one fn");
154+
assert!(!self.found_unsafe, "We should never see a fn when we already saw unsafe");
117155
let is_unsafe = match fk {
118156
intravisit::FnKind::ItemFn(_, _, unsafety, ..) => unsafety == hir::Unsafety::Unsafe,
119157
intravisit::FnKind::Method(_, sig, ..) => sig.unsafety == hir::Unsafety::Unsafe,
@@ -129,20 +167,15 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
129167
}
130168

131169
fn visit_block(&mut self, b: &'tcx hir::Block) {
132-
use rustc::hir::BlockCheckMode::*;
133-
134170
if self.found_unsafe { return; } // short-circuit
135171

136-
match b.rules {
137-
UnsafeBlock(_) | PushUnsafeBlock(_) => {
138-
// We found an unsafe block.
139-
self.found_unsafe = true;
140-
}
141-
DefaultBlock | PopUnsafeBlock(_) => {
142-
// No unsafe block here, go on searching.
143-
intravisit::walk_block(self, b);
144-
}
145-
};
172+
if block_is_unsafe(b) {
173+
// We found an unsafe block. We can stop searching.
174+
self.found_unsafe = true;
175+
} else {
176+
// No unsafe block here, go on searching.
177+
intravisit::walk_block(self, b);
178+
}
146179
}
147180
}
148181

src/test/mir-opt/validate_1.rs

+7
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@ impl Test {
2121
fn main() {
2222
let mut x = 0;
2323
Test.foo(&mut x);
24+
25+
// Also test closures
26+
let c = |x: &mut i32| { let y = &*x; *y };
27+
c(&mut x);
2428
}
2529

30+
// FIXME: Also test code generated inside the closure, make sure it has validation. Unfortunately,
31+
// the interesting lines of code also contain name of the source file, so we cannot test for it.
32+
2633
// END RUST SOURCE
2734
// START rustc.node10.EraseRegions.after.mir
2835
// bb0: {

src/test/mir-opt/validate_4.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-tidy-linelength
12+
// compile-flags: -Z verbose -Z mir-emit-validate=1
13+
14+
// Make sure unsafe fns and fns with an unsafe block only get restricted validation.
15+
16+
unsafe fn write_42(x: *mut i32) -> bool {
17+
*x = 42;
18+
true
19+
}
20+
21+
fn test(x: &mut i32) {
22+
unsafe { write_42(x) };
23+
}
24+
25+
fn main() {
26+
test(&mut 0);
27+
28+
let test_closure = unsafe { |x: &mut i32| write_42(x) };
29+
test_closure(&mut 0);
30+
}
31+
32+
// FIXME: Also test code generated inside the closure, make sure it only does restricted validation
33+
// because it is entirely inside an unsafe block. Unfortunately, the interesting lines of code also
34+
// contain name of the source file, so we cannot test for it.
35+
36+
// END RUST SOURCE
37+
// START rustc.node4.EraseRegions.after.mir
38+
// fn write_42(_1: *mut i32) -> bool {
39+
// bb0: {
40+
// Validate(Acquire, [_1: *mut i32]);
41+
// Validate(Release, [_1: *mut i32]);
42+
// return;
43+
// }
44+
// }
45+
// END rustc.node4.EraseRegions.after.mir
46+
// START rustc.node17.EraseRegions.after.mir
47+
// fn test(_1: &ReErased mut i32) -> () {
48+
// bb0: {
49+
// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]);
50+
// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]);
51+
// _3 = const write_42(_4) -> bb1;
52+
// }
53+
// bb1: {
54+
// Validate(Acquire, [_3: bool]);
55+
// Validate(Release, [_3: bool]);
56+
// }
57+
// }
58+
// END rustc.node17.EraseRegions.after.mir

src/test/mir-opt/validate_5.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-tidy-linelength
12+
// compile-flags: -Z verbose -Z mir-emit-validate=2
13+
14+
// Make sure unsafe fns and fns with an unsafe block only get full validation.
15+
16+
unsafe fn write_42(x: *mut i32) -> bool {
17+
*x = 42;
18+
true
19+
}
20+
21+
fn test(x: &mut i32) {
22+
unsafe { write_42(x) };
23+
}
24+
25+
fn main() {
26+
test(&mut 0);
27+
28+
let test_closure = unsafe { |x: &mut i32| write_42(x) };
29+
test_closure(&mut 0);
30+
}
31+
32+
// FIXME: Also test code generated inside the closure, make sure it has validation. Unfortunately,
33+
// the interesting lines of code also contain name of the source file, so we cannot test for it.
34+
35+
// END RUST SOURCE
36+
// START rustc.node17.EraseRegions.after.mir
37+
// fn test(_1: &ReErased mut i32) -> () {
38+
// bb0: {
39+
// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(4) => validate_5/8cd878b::test[0] }, BrAnon(0)) mut i32]);
40+
// Validate(Release, [_4: *mut i32]);
41+
// _3 = const write_42(_4) -> bb1;
42+
// }
43+
// }
44+
// END rustc.node17.EraseRegions.after.mir

0 commit comments

Comments
 (0)