Skip to content

Commit c5154d0

Browse files
committed
use FnLike to recognize functions for us
1 parent 8f910bc commit c5154d0

File tree

2 files changed

+72
-63
lines changed

2 files changed

+72
-63
lines changed

src/librustc/hir/map/blocks.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ impl<'a> FnLikeNode<'a> {
192192
}
193193
}
194194

195+
pub fn unsafety(self) -> ast::Unsafety {
196+
match self.kind() {
197+
FnKind::ItemFn(_, _, unsafety, ..) => {
198+
unsafety
199+
}
200+
FnKind::Method(_, m, ..) => {
201+
m.unsafety
202+
}
203+
_ => ast::Unsafety::Normal
204+
}
205+
}
206+
195207
pub fn kind(self) -> FnKind<'a> {
196208
let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
197209
FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis, p.attrs)

src/librustc_mir/transform/add_validation.rs

Lines changed: 60 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
//! of MIR building, and only after this pass we think of the program has having the
1515
//! normal MIR semantics.
1616
17-
use syntax_pos::Span;
18-
use syntax::ast::NodeId;
1917
use rustc::ty::{self, TyCtxt, RegionKind};
2018
use rustc::hir;
2119
use rustc::mir::*;
@@ -84,9 +82,11 @@ fn lval_context<'a, 'tcx, D>(
8482

8583
/// Check if this function contains an unsafe block or is an unsafe function.
8684
fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> bool {
87-
use rustc::hir::intravisit::{self, Visitor};
85+
use rustc::hir::intravisit::{self, Visitor, FnKind};
86+
use rustc::hir::map::blocks::FnLikeNode;
8887
use rustc::hir::map::Node;
8988

89+
/// Decide if this is an unsafe block
9090
fn block_is_unsafe(block: &hir::Block) -> bool {
9191
use rustc::hir::BlockCheckMode::*;
9292

@@ -98,77 +98,74 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) ->
9898
}
9999
}
100100

101-
let fn_node_id = match src {
102-
MirSource::Fn(node_id) => node_id,
101+
/// Decide if this FnLike is a closure
102+
fn fn_is_closure<'a>(fn_like: FnLikeNode<'a>) -> bool {
103+
match fn_like.kind() {
104+
FnKind::Closure(_) => true,
105+
FnKind::Method(..) | FnKind::ItemFn(..) => false,
106+
}
107+
}
108+
109+
let fn_like = match src {
110+
MirSource::Fn(node_id) => {
111+
match FnLikeNode::from_node(tcx.hir.get(node_id)) {
112+
Some(fn_like) => fn_like,
113+
None => return false, // e.g. struct ctor shims -- such auto-generated code cannot
114+
// contain unsafe.
115+
}
116+
},
103117
_ => return false, // only functions can have unsafe
104118
};
105119

106-
struct FindUnsafe<'b, 'tcx> where 'tcx : 'b {
107-
map: &'b hir::map::Map<'tcx>,
108-
found_unsafe: bool,
120+
// Test if the function is marked unsafe.
121+
if fn_like.unsafety() == hir::Unsafety::Unsafe {
122+
return true;
109123
}
110-
let mut finder = FindUnsafe { map: &tcx.hir, found_unsafe: false };
111-
// Run the visitor on the NodeId we got. Seems like there is no uniform way to do that.
112-
match tcx.hir.find(fn_node_id) {
113-
Some(Node::NodeItem(item)) => finder.visit_item(item),
114-
Some(Node::NodeImplItem(item)) => finder.visit_impl_item(item),
115-
Some(Node::NodeTraitItem(item)) => finder.visit_trait_item(item),
116-
Some(Node::NodeExpr(item)) => {
117-
// This is a closure.
118-
// We also have to walk up the parents and check that there is no unsafe block
119-
// there.
120-
let mut cur = fn_node_id;
121-
loop {
122-
// Go further upwards.
123-
let parent = tcx.hir.get_parent_node(cur);
124-
if cur == parent {
125-
break;
124+
125+
// For closures, we need to walk up the parents and see if we are inside an unsafe fn or
126+
// unsafe block.
127+
if fn_is_closure(fn_like) {
128+
let mut cur = fn_like.id();
129+
loop {
130+
// Go further upwards.
131+
let parent = tcx.hir.get_parent_node(cur);
132+
if cur == parent {
133+
bug!("Closures muts be inside a non-closure fn_like");
134+
}
135+
cur = parent;
136+
// Check if this is an unsafe block
137+
match tcx.hir.find(cur) {
138+
Some(Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..})) => {
139+
if block_is_unsafe(&*block) {
140+
// Found an unsafe block, we can bail out here.
141+
return true;
142+
}
126143
}
127-
cur = parent;
128-
// Check if this is a a block
129-
match tcx.hir.find(cur) {
130-
Some(Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block), ..})) => {
131-
if block_is_unsafe(&*block) {
132-
// Found an unsafe block, we can bail out here.
133-
return true;
134-
}
144+
_ => {},
145+
}
146+
// Check if this is a non-closure fn_like, at which point we have to stop moving up
147+
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(cur)) {
148+
if !fn_is_closure(fn_like) {
149+
if fn_like.unsafety() == hir::Unsafety::Unsafe {
150+
return true;
135151
}
136-
_ => {},
152+
break;
137153
}
138154
}
139-
// Finally, visit the closure itself.
140-
finder.visit_expr(item);
141155
}
142-
Some(Node::NodeStructCtor(_)) => {
143-
// Auto-generated tuple struct ctor. Cannot contain unsafe code.
144-
return false;
145-
},
146-
Some(_) | None =>
147-
bug!("Expected function, method or closure, found {}",
148-
tcx.hir.node_to_string(fn_node_id)),
149-
};
156+
}
150157

151-
impl<'b, 'tcx> Visitor<'tcx> for FindUnsafe<'b, 'tcx> {
152-
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
153-
intravisit::NestedVisitorMap::OnlyBodies(self.map)
154-
}
158+
// Visit the entire body of the function and check for unsafe blocks in there
159+
struct FindUnsafe {
160+
found_unsafe: bool,
161+
}
162+
let mut finder = FindUnsafe { found_unsafe: false };
163+
// Run the visitor on the NodeId we got. Seems like there is no uniform way to do that.
164+
finder.visit_body(tcx.hir.body(fn_like.body()));
155165

156-
fn visit_fn(&mut self, fk: intravisit::FnKind<'tcx>, fd: &'tcx hir::FnDecl,
157-
b: hir::BodyId, s: Span, id: NodeId)
158-
{
159-
assert!(!self.found_unsafe, "We should never see a fn when we already saw unsafe");
160-
let is_unsafe = match fk {
161-
intravisit::FnKind::ItemFn(_, _, unsafety, ..) => unsafety == hir::Unsafety::Unsafe,
162-
intravisit::FnKind::Method(_, sig, ..) => sig.unsafety == hir::Unsafety::Unsafe,
163-
intravisit::FnKind::Closure(_) => false,
164-
};
165-
if is_unsafe {
166-
// This is unsafe, and we are done.
167-
self.found_unsafe = true;
168-
} else {
169-
// Go on searching.
170-
intravisit::walk_fn(self, fk, fd, b, s, id)
171-
}
166+
impl<'tcx> Visitor<'tcx> for FindUnsafe {
167+
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
168+
intravisit::NestedVisitorMap::None
172169
}
173170

174171
fn visit_block(&mut self, b: &'tcx hir::Block) {

0 commit comments

Comments
 (0)