Skip to content

Commit aeda178

Browse files
committed
librustc: Redo the unsafe checker and make unsafe methods not callable from safe code
1 parent b5da389 commit aeda178

16 files changed

+288
-136
lines changed

src/libextra/arc.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,15 @@ pub impl<T:Owned> MutexARC<T> {
198198
*/
199199
#[inline(always)]
200200
unsafe fn access<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
201-
let state = self.x.get();
202-
// Borrowck would complain about this if the function were
203-
// not already unsafe. See borrow_rwlock, far below.
204-
do (&(*state).lock).lock {
205-
check_poison(true, (*state).failed);
206-
let _z = PoisonOnFail(&mut (*state).failed);
207-
blk(&mut (*state).data)
201+
unsafe {
202+
let state = self.x.get();
203+
// Borrowck would complain about this if the function were
204+
// not already unsafe. See borrow_rwlock, far below.
205+
do (&(*state).lock).lock {
206+
check_poison(true, (*state).failed);
207+
let _z = PoisonOnFail(&mut (*state).failed);
208+
blk(&mut (*state).data)
209+
}
208210
}
209211
}
210212

@@ -356,8 +358,8 @@ pub impl<T:Const + Owned> RWARC<T> {
356358
* access modes, this will not poison the ARC.
357359
*/
358360
fn read<U>(&self, blk: &fn(x: &T) -> U) -> U {
359-
let state = self.x.get();
360361
unsafe {
362+
let state = self.x.get();
361363
do (*state).lock.read {
362364
check_poison(false, (*state).failed);
363365
blk(&(*state).data)

src/libextra/sync.rs

+51-43
Original file line numberDiff line numberDiff line change
@@ -100,30 +100,34 @@ fn new_sem_and_signal(count: int, num_condvars: uint)
100100
#[doc(hidden)]
101101
pub impl<Q:Owned> Sem<Q> {
102102
fn acquire(&self) {
103-
let mut waiter_nobe = None;
104-
do (**self).with |state| {
105-
state.count -= 1;
106-
if state.count < 0 {
107-
// Create waiter nobe.
108-
let (WaitEnd, SignalEnd) = comm::oneshot();
109-
// Tell outer scope we need to block.
110-
waiter_nobe = Some(WaitEnd);
111-
// Enqueue ourself.
112-
state.waiters.tail.send(SignalEnd);
103+
unsafe {
104+
let mut waiter_nobe = None;
105+
do (**self).with |state| {
106+
state.count -= 1;
107+
if state.count < 0 {
108+
// Create waiter nobe.
109+
let (WaitEnd, SignalEnd) = comm::oneshot();
110+
// Tell outer scope we need to block.
111+
waiter_nobe = Some(WaitEnd);
112+
// Enqueue ourself.
113+
state.waiters.tail.send(SignalEnd);
114+
}
115+
}
116+
// Uncomment if you wish to test for sem races. Not valgrind-friendly.
117+
/* for 1000.times { task::yield(); } */
118+
// Need to wait outside the exclusive.
119+
if waiter_nobe.is_some() {
120+
let _ = comm::recv_one(waiter_nobe.unwrap());
113121
}
114-
}
115-
// Uncomment if you wish to test for sem races. Not valgrind-friendly.
116-
/* for 1000.times { task::yield(); } */
117-
// Need to wait outside the exclusive.
118-
if waiter_nobe.is_some() {
119-
let _ = comm::recv_one(waiter_nobe.unwrap());
120122
}
121123
}
122124
fn release(&self) {
123-
do (**self).with |state| {
124-
state.count += 1;
125-
if state.count <= 0 {
126-
signal_waitqueue(&state.waiters);
125+
unsafe {
126+
do (**self).with |state| {
127+
state.count += 1;
128+
if state.count <= 0 {
129+
signal_waitqueue(&state.waiters);
130+
}
127131
}
128132
}
129133
}
@@ -283,17 +287,19 @@ pub impl<'self> Condvar<'self> {
283287

284288
/// As signal, but with a specified condvar_id. See wait_on.
285289
fn signal_on(&self, condvar_id: uint) -> bool {
286-
let mut out_of_bounds = None;
287-
let mut result = false;
288-
do (**self.sem).with |state| {
289-
if condvar_id < state.blocked.len() {
290-
result = signal_waitqueue(&state.blocked[condvar_id]);
291-
} else {
292-
out_of_bounds = Some(state.blocked.len());
290+
unsafe {
291+
let mut out_of_bounds = None;
292+
let mut result = false;
293+
do (**self.sem).with |state| {
294+
if condvar_id < state.blocked.len() {
295+
result = signal_waitqueue(&state.blocked[condvar_id]);
296+
} else {
297+
out_of_bounds = Some(state.blocked.len());
298+
}
299+
}
300+
do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") {
301+
result
293302
}
294-
}
295-
do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") {
296-
result
297303
}
298304
}
299305

@@ -304,20 +310,22 @@ pub impl<'self> Condvar<'self> {
304310
fn broadcast_on(&self, condvar_id: uint) -> uint {
305311
let mut out_of_bounds = None;
306312
let mut queue = None;
307-
do (**self.sem).with |state| {
308-
if condvar_id < state.blocked.len() {
309-
// To avoid :broadcast_heavy, we make a new waitqueue,
310-
// swap it out with the old one, and broadcast on the
311-
// old one outside of the little-lock.
312-
queue = Some(util::replace(&mut state.blocked[condvar_id],
313-
new_waitqueue()));
314-
} else {
315-
out_of_bounds = Some(state.blocked.len());
313+
unsafe {
314+
do (**self.sem).with |state| {
315+
if condvar_id < state.blocked.len() {
316+
// To avoid :broadcast_heavy, we make a new waitqueue,
317+
// swap it out with the old one, and broadcast on the
318+
// old one outside of the little-lock.
319+
queue = Some(util::replace(&mut state.blocked[condvar_id],
320+
new_waitqueue()));
321+
} else {
322+
out_of_bounds = Some(state.blocked.len());
323+
}
324+
}
325+
do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") {
326+
let queue = queue.swap_unwrap();
327+
broadcast_waitqueue(&queue)
316328
}
317-
}
318-
do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") {
319-
let queue = queue.swap_unwrap();
320-
broadcast_waitqueue(&queue)
321329
}
322330
}
323331
}

src/librustc/driver/driver.rs

+3
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ pub fn compile_rest(sess: Session,
262262
time(time_passes, ~"privacy checking", ||
263263
middle::privacy::check_crate(ty_cx, &method_map, crate));
264264

265+
time(time_passes, ~"effect checking", ||
266+
middle::effect::check_crate(ty_cx, method_map, crate));
267+
265268
time(time_passes, ~"loop checking", ||
266269
middle::check_loop::check_crate(ty_cx, crate));
267270

src/librustc/middle/effect.rs

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright 2012-2013 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+
//! Enforces the Rust effect system. Currently there is just one effect,
12+
/// `unsafe`.
13+
14+
use middle::ty::{ty_bare_fn, ty_closure, ty_ptr};
15+
use middle::ty;
16+
use middle::typeck::method_map;
17+
use util::ppaux;
18+
19+
use syntax::ast::{deref, expr_call, expr_inline_asm, expr_method_call};
20+
use syntax::ast::{expr_unary, node_id, unsafe_blk, unsafe_fn};
21+
use syntax::ast;
22+
use syntax::codemap::span;
23+
use syntax::visit::{fk_item_fn, fk_method};
24+
use syntax::visit;
25+
26+
#[deriving(Eq)]
27+
enum UnsafeContext {
28+
SafeContext,
29+
UnsafeFn,
30+
UnsafeBlock(node_id),
31+
}
32+
33+
struct Context {
34+
/// The method map.
35+
method_map: method_map,
36+
/// Whether we're in an unsafe context.
37+
unsafe_context: UnsafeContext,
38+
}
39+
40+
fn type_is_unsafe_function(ty: ty::t) -> bool {
41+
match ty::get(ty).sty {
42+
ty_bare_fn(ref f) => f.purity == unsafe_fn,
43+
ty_closure(ref f) => f.purity == unsafe_fn,
44+
_ => false,
45+
}
46+
}
47+
48+
pub fn check_crate(tcx: ty::ctxt,
49+
method_map: method_map,
50+
crate: @ast::crate) {
51+
let context = @mut Context {
52+
method_map: method_map,
53+
unsafe_context: SafeContext,
54+
};
55+
56+
let require_unsafe: @fn(span: span,
57+
description: &str) = |span, description| {
58+
match context.unsafe_context {
59+
SafeContext => {
60+
// Report an error.
61+
tcx.sess.span_err(span,
62+
fmt!("%s requires unsafe function or block",
63+
description))
64+
}
65+
UnsafeBlock(block_id) => {
66+
// OK, but record this.
67+
debug!("effect: recording unsafe block as used: %?", block_id);
68+
let _ = tcx.used_unsafe.insert(block_id);
69+
}
70+
UnsafeFn => {}
71+
}
72+
};
73+
74+
let visitor = visit::mk_vt(@visit::Visitor {
75+
visit_fn: |fn_kind, fn_decl, block, span, node_id, _, visitor| {
76+
let is_unsafe_fn = match *fn_kind {
77+
fk_item_fn(_, _, purity, _) => purity == unsafe_fn,
78+
fk_method(_, _, method) => method.purity == unsafe_fn,
79+
_ => false,
80+
};
81+
82+
let old_unsafe_context = context.unsafe_context;
83+
if is_unsafe_fn {
84+
context.unsafe_context = UnsafeFn
85+
}
86+
87+
visit::visit_fn(fn_kind,
88+
fn_decl,
89+
block,
90+
span,
91+
node_id,
92+
(),
93+
visitor);
94+
95+
context.unsafe_context = old_unsafe_context
96+
},
97+
98+
visit_block: |block, _, visitor| {
99+
let old_unsafe_context = context.unsafe_context;
100+
if block.node.rules == unsafe_blk {
101+
context.unsafe_context = UnsafeBlock(block.node.id)
102+
}
103+
104+
visit::visit_block(block, (), visitor);
105+
106+
context.unsafe_context = old_unsafe_context
107+
},
108+
109+
visit_expr: |expr, _, visitor| {
110+
match expr.node {
111+
expr_method_call(*) => {
112+
let base_type = ty::node_id_to_type(tcx, expr.callee_id);
113+
debug!("effect: method call case, base type is %s",
114+
ppaux::ty_to_str(tcx, base_type));
115+
if type_is_unsafe_function(base_type) {
116+
require_unsafe(expr.span,
117+
"invocation of unsafe method")
118+
}
119+
}
120+
expr_call(base, _, _) => {
121+
let base_type = ty::node_id_to_type(tcx, base.id);
122+
debug!("effect: call case, base type is %s",
123+
ppaux::ty_to_str(tcx, base_type));
124+
if type_is_unsafe_function(base_type) {
125+
require_unsafe(expr.span, "call to unsafe function")
126+
}
127+
}
128+
expr_unary(deref, base) => {
129+
let base_type = ty::node_id_to_type(tcx, base.id);
130+
debug!("effect: unary case, base type is %s",
131+
ppaux::ty_to_str(tcx, base_type));
132+
match ty::get(base_type).sty {
133+
ty_ptr(_) => {
134+
require_unsafe(expr.span,
135+
"dereference of unsafe pointer")
136+
}
137+
_ => {}
138+
}
139+
}
140+
expr_inline_asm(*) => {
141+
require_unsafe(expr.span, "use of inline assembly")
142+
}
143+
_ => {}
144+
}
145+
146+
visit::visit_expr(expr, (), visitor)
147+
},
148+
149+
.. *visit::default_visitor()
150+
});
151+
152+
visit::visit_crate(crate, (), visitor)
153+
}
154+

src/librustc/middle/typeck/check/mod.rs

-34
Original file line numberDiff line numberDiff line change
@@ -891,21 +891,6 @@ pub impl FnCtxt {
891891
infer::mk_subr(self.infcx(), a_is_expected, span, sub, sup)
892892
}
893893

894-
fn require_unsafe(&self, sp: span, op: ~str) {
895-
match self.ps.purity {
896-
ast::unsafe_fn => {
897-
// ok, but flag that we used the source of unsafeness
898-
debug!("flagging %? as a used unsafe source", self.ps);
899-
self.tcx().used_unsafe.insert(self.ps.def);
900-
}
901-
_ => {
902-
self.ccx.tcx.sess.span_err(
903-
sp,
904-
fmt!("%s requires unsafe function or block", op));
905-
}
906-
}
907-
}
908-
909894
fn with_region_lb<R>(@mut self, lb: ast::node_id, f: &fn() -> R) -> R {
910895
let old_region_lb = self.region_lb;
911896
self.region_lb = lb;
@@ -2285,16 +2270,6 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
22852270
}
22862271
ast::deref => {
22872272
let sty = structure_of(fcx, expr.span, oprnd_t);
2288-
match sty {
2289-
// deref'ing an unsafe pointer requires that we be in
2290-
// an unsafe context
2291-
ty::ty_ptr(*) => {
2292-
fcx.require_unsafe(
2293-
expr.span,
2294-
~"dereference of unsafe pointer");
2295-
}
2296-
_ => { /*ok*/ }
2297-
}
22982273
let operand_ty = ty::deref_sty(tcx, &sty, true);
22992274
match operand_ty {
23002275
Some(mt) => {
@@ -2392,8 +2367,6 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
23922367
fcx.write_ty(id, ty_param_bounds_and_ty.ty);
23932368
}
23942369
ast::expr_inline_asm(ref ia) => {
2395-
fcx.require_unsafe(expr.span, ~"use of inline assembly");
2396-
23972370
for ia.inputs.each |&(_, in)| {
23982371
check_expr(fcx, in);
23992372
}
@@ -3223,13 +3196,6 @@ pub fn ty_param_bounds_and_ty_for_def(fcx: @mut FnCtxt,
32233196
};
32243197
}
32253198

3226-
ast::def_fn(id, ast::unsafe_fn) |
3227-
ast::def_static_method(id, _, ast::unsafe_fn) => {
3228-
// Unsafe functions can only be touched in an unsafe context
3229-
fcx.require_unsafe(sp, ~"access to unsafe function");
3230-
return ty::lookup_item_type(fcx.ccx.tcx, id);
3231-
}
3232-
32333199
ast::def_fn(id, _) | ast::def_static_method(id, _, _) |
32343200
ast::def_const(id) | ast::def_variant(_, id) |
32353201
ast::def_struct(id) => {

src/librustc/rustc.rc

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub mod middle {
109109
pub mod privacy;
110110
pub mod moves;
111111
pub mod entry;
112+
pub mod effect;
112113
}
113114

114115
pub mod front {

0 commit comments

Comments
 (0)