Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/comp/driver/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ options:
--test build test harness
--gc garbage collect shared data (experimental/temporary)
--stack-growth perform stack checks (experimental)
--check-unsafe disallow unsafe actions in non-unsafe functions

");
}
Expand Down Expand Up @@ -322,6 +323,7 @@ fn build_session_options(match: getopts::match)

let parse_only = opt_present(match, "parse-only");
let no_trans = opt_present(match, "no-trans");
let check_unsafe = opt_present(match, "check-unsafe");

let output_type =
if parse_only || no_trans {
Expand Down Expand Up @@ -393,7 +395,8 @@ fn build_session_options(match: getopts::match)
parse_only: parse_only,
no_trans: no_trans,
do_gc: do_gc,
stack_growth: stack_growth};
stack_growth: stack_growth,
check_unsafe: check_unsafe};
ret sopts;
}

Expand Down Expand Up @@ -432,7 +435,7 @@ fn opts() -> [getopts::opt] {
optflag("no-typestate"), optflag("noverify"),
optmulti("cfg"), optflag("test"),
optflag("lib"), optflag("static"), optflag("gc"),
optflag("stack-growth")];
optflag("stack-growth"), optflag("check-unsafe")];
}

fn main(args: [str]) {
Expand Down
3 changes: 2 additions & 1 deletion src/comp/driver/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ type options =
parse_only: bool,
no_trans: bool,
do_gc: bool,
stack_growth: bool};
stack_growth: bool,
check_unsafe: bool};

type crate_metadata = {name: str, data: [u8]};

Expand Down
5 changes: 3 additions & 2 deletions src/comp/front/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ fn mk_tests(cx: test_ctxt) -> @ast::item {
let test_descs = mk_test_desc_vec(cx);

let body_: ast::blk_ =
checked_blk([], option::some(test_descs), cx.next_node_id());
default_block([], option::some(test_descs), cx.next_node_id());
let body = nospan(body_);

let fn_ = {decl: decl, proto: proto, body: body};
Expand Down Expand Up @@ -303,7 +303,8 @@ fn mk_main(cx: test_ctxt) -> @ast::item {
let test_main_call_expr = mk_test_main_call(cx);

let body_: ast::blk_ =
checked_blk([], option::some(test_main_call_expr), cx.next_node_id());
default_block([], option::some(test_main_call_expr),
cx.next_node_id());
let body = {node: body_, span: dummy_sp()};

let fn_ = {decl: decl, proto: proto, body: body};
Expand Down
3 changes: 3 additions & 0 deletions src/comp/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ fn lookup_def(cnum: ast::crate_num, data: @[u8], did_: ast::def_id) ->
let def =
alt fam_ch as char {
'c' { ast::def_const(did) }
'u' { ast::def_fn(did, ast::unsafe_fn) }
'f' { ast::def_fn(did, ast::impure_fn) }
'p' { ast::def_fn(did, ast::pure_fn) }
'F' { ast::def_native_fn(did) }
Expand Down Expand Up @@ -250,6 +251,7 @@ fn family_has_type_params(fam_ch: u8) -> bool {
ret alt fam_ch as char {
'c' { false }
'f' { true }
'u' { true }
'p' { true }
'F' { true }
'y' { true }
Expand Down Expand Up @@ -278,6 +280,7 @@ fn item_family_to_str(fam: u8) -> str {
alt fam as char {
'c' { ret "const"; }
'f' { ret "fn"; }
'u' { ret "unsafe fn"; }
'p' { ret "pure fn"; }
'F' { ret "native fn"; }
'y' { ret "type"; }
Expand Down
1 change: 1 addition & 0 deletions src/comp/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
encode_def_id(ebml_w, local_def(item.id));
encode_family(ebml_w,
alt fd.decl.purity {
unsafe_fn. { 'u' }
pure_fn. { 'p' }
impure_fn. { 'f' }
} as u8);
Expand Down
40 changes: 22 additions & 18 deletions src/comp/middle/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ tag ret_info { by_ref(bool, node_id); other; }
type scope = {bs: [binding], ret_info: ret_info};

fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option::t<node_id>,
unsafe: [unsafe_ty]) -> binding {
unsafe_tys: [unsafe_ty]) -> binding {
ret @{node_id: id, span: span, root_var: root_var,
local_id: local_id_of_node(cx, id),
unsafe_tys: unsafe, mutable ok: valid,
unsafe_tys: unsafe_tys, mutable ok: valid,
mutable copied: not_copied};
}

Expand Down Expand Up @@ -284,12 +284,12 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] {
}
let j = 0u;
for b in bindings {
for unsafe in b.unsafe_tys {
for unsafe_ty in b.unsafe_tys {
let i = 0u;
for arg_t: ty::arg in arg_ts {
let mut_alias = arg_t.mode == ast::by_mut_ref;
if i != j &&
ty_can_unsafely_include(cx, unsafe, arg_t.ty,
ty_can_unsafely_include(cx, unsafe_ty, arg_t.ty,
mut_alias) &&
cant_copy(cx, b) {
cx.tcx.sess.span_err
Expand Down Expand Up @@ -397,24 +397,28 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
let new_bs = sc.bs;
let root_var = path_def_id(cx, root.ex);
let pat_id_map = ast_util::pat_id_map(a.pats[0]);
type info = {id: node_id, mutable unsafe: [unsafe_ty], span: span};
type info = {
id: node_id,
mutable unsafe_tys: [unsafe_ty],
span: span};
let binding_info: [info] = [];
for pat in a.pats {
for proot in pattern_roots(cx.tcx, root.mut, pat) {
let canon_id = pat_id_map.get(proot.name);
alt vec::find({|x| x.id == canon_id}, binding_info) {
some(s) { s.unsafe += unsafe_set(proot.mut); }
some(s) { s.unsafe_tys += unsafe_set(proot.mut); }
none. {
binding_info += [{id: canon_id,
mutable unsafe: unsafe_set(proot.mut),
span: proot.span}];
binding_info += [
{id: canon_id,
mutable unsafe_tys: unsafe_set(proot.mut),
span: proot.span}];
}
}
}
}
for info in binding_info {
new_bs += [mk_binding(cx, info.id, info.span, root_var,
copy info.unsafe)];
copy info.unsafe_tys)];
}
visit::visit_arm(a, {bs: new_bs with sc}, v);
}
Expand Down Expand Up @@ -470,8 +474,8 @@ fn check_var(cx: ctx, ex: @ast::expr, p: ast::path, id: ast::node_id,
for b in sc.bs {
// excludes variables introduced since the alias was made
if my_local_id < b.local_id {
for unsafe in b.unsafe_tys {
if ty_can_unsafely_include(cx, unsafe, var_t, assign) {
for unsafe_ty in b.unsafe_tys {
if ty_can_unsafely_include(cx, unsafe_ty, var_t, assign) {
b.ok = val_taken(ex.span, p);
}
}
Expand Down Expand Up @@ -689,9 +693,9 @@ fn pattern_roots(tcx: ty::ctxt, mut: option::t<unsafe_ty>, pat: @ast::pat)
fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool)
-> {ex: @ast::expr, mut: option::t<unsafe_ty>} {
let base_root = mut::expr_root(cx.tcx, ex, autoderef);
let unsafe = none;
let unsafe_ty = none;
for d in *base_root.ds {
if d.mut { unsafe = some(contains(d.outer_t)); break; }
if d.mut { unsafe_ty = some(contains(d.outer_t)); break; }
}
if is_none(path_def_id(cx, base_root.ex)) {
alt base_root.ex.node {
Expand All @@ -703,18 +707,18 @@ fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool)
let arg_root = expr_root(cx, arg, false);
if mut {
let ret_ty = ty::expr_ty(cx.tcx, base_root.ex);
unsafe = some(mut_contains(ret_ty));
unsafe_ty = some(mut_contains(ret_ty));
}
if !is_none(arg_root.mut) { unsafe = arg_root.mut; }
ret {ex: arg_root.ex, mut: unsafe};
if !is_none(arg_root.mut) { unsafe_ty = arg_root.mut; }
ret {ex: arg_root.ex, mut: unsafe_ty};
}
_ {}
}
}
_ {}
}
}
ret {ex: base_root.ex, mut: unsafe};
ret {ex: base_root.ex, mut: unsafe_ty};
}

fn unsafe_set(from: option::t<unsafe_ty>) -> [unsafe_ty] {
Expand Down
4 changes: 2 additions & 2 deletions src/comp/middle/trans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4367,11 +4367,11 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
assert dest == ignore;
ret trans_check_expr(bcx, a, "Assertion");
}
ast::expr_check(ast::checked., a) {
ast::expr_check(ast::checked_expr., a) {
assert dest == ignore;
ret trans_check_expr(bcx, a, "Predicate");
}
ast::expr_check(ast::unchecked., a) {
ast::expr_check(ast::claimed_expr., a) {
assert dest == ignore;
/* Claims are turned on and off by a global variable
that the RTS sets. This case generates code to
Expand Down
52 changes: 44 additions & 8 deletions src/comp/middle/typeck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1523,8 +1523,22 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
}
}

fn require_unsafe(sess: session::session, f_purity: ast::purity, sp: span) {
if sess.get_opts().check_unsafe {
alt f_purity {
ast::unsafe_fn. { ret; }
_ {
sess.span_fatal(
sp,
"Found unsafe expression in safe function decl");
}
}
}
}

fn require_impure(sess: session::session, f_purity: ast::purity, sp: span) {
alt f_purity {
ast::unsafe_fn. { ret; }
ast::impure_fn. { ret; }
ast::pure_fn. {
sess.span_fatal(sp, "Found impure expression in pure function decl");
Expand All @@ -1535,7 +1549,29 @@ fn require_impure(sess: session::session, f_purity: ast::purity, sp: span) {
fn require_pure_call(ccx: @crate_ctxt, caller_purity: ast::purity,
callee: @ast::expr, sp: span) {
alt caller_purity {
ast::impure_fn. { ret; }
ast::unsafe_fn. { ret; }
ast::impure_fn. {
let sess = ccx.tcx.sess;
alt ccx.tcx.def_map.find(callee.id) {
some(ast::def_fn(_, ast::unsafe_fn.)) {
if sess.get_opts().check_unsafe {
ccx.tcx.sess.span_fatal(
sp,
"safe function calls function marked unsafe");
}
}
some(ast::def_native_fn(_)) {
if sess.get_opts().check_unsafe {
ccx.tcx.sess.span_fatal(
sp,
"native functions can only be invoked from unsafe code");
}
}
_ {
}
}
ret;
}
ast::pure_fn. {
alt ccx.tcx.def_map.find(callee.id) {
some(ast::def_fn(_, ast::pure_fn.)) { ret; }
Expand Down Expand Up @@ -2063,12 +2099,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
ast::expr_block(b) {
// If this is an unchecked block, turn off purity-checking
let fcx_for_block =
alt b.node.rules {
ast::unchecked. { @{purity: ast::impure_fn with *fcx} }
_ { fcx }
};
bot = check_block(fcx_for_block, b);
bot = check_block(fcx, b);
let typ =
alt b.node.expr {
some(expr) { expr_ty(tcx, expr) }
Expand Down Expand Up @@ -2514,7 +2545,12 @@ fn check_stmt(fcx: @fn_ctxt, stmt: @ast::stmt) -> bool {
ret bot;
}

fn check_block(fcx: @fn_ctxt, blk: ast::blk) -> bool {
fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
let fcx = alt blk.node.rules {
ast::unchecked_blk. { @{purity: ast::impure_fn with *fcx0} }
ast::unsafe_blk. { @{purity: ast::unsafe_fn with *fcx0} }
ast::default_blk. { fcx0 }
};
let bot = false;
let warned = false;
for s: @ast::stmt in blk.node.stmts {
Expand Down
10 changes: 7 additions & 3 deletions src/comp/syntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ tag meta_item_ {
type blk = spanned<blk_>;

type blk_ =
{stmts: [@stmt], expr: option::t<@expr>, id: node_id, rules: check_mode};
{stmts: [@stmt], expr: option::t<@expr>, id: node_id,
rules: blk_check_mode};

type pat = {id: node_id, node: pat_, span: span};

Expand Down Expand Up @@ -165,7 +166,9 @@ type field_ = {mut: mutability, ident: ident, expr: @expr};

type field = spanned<field_>;

tag check_mode { checked; unchecked; }
tag blk_check_mode { default_blk; unchecked_blk; unsafe_blk; }

tag expr_check_mode { claimed_expr; checked_expr; }

type expr = {id: node_id, node: expr_, span: span};

Expand Down Expand Up @@ -214,7 +217,7 @@ tag expr_ {
expr_assert(@expr);

/* preds that typestate is aware of */
expr_check(check_mode, @expr);
expr_check(expr_check_mode, @expr);

/* FIXME Would be nice if expr_check desugared
to expr_if_check. */
Expand Down Expand Up @@ -382,6 +385,7 @@ type fn_decl =

tag purity {
pure_fn; // declared with "pure fn"
unsafe_fn; // declared with "unsafe fn"
impure_fn; // declared with "fn"
}

Expand Down
6 changes: 3 additions & 3 deletions src/comp/syntax/ast_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,13 @@ fn eq_ty(&&a: @ty, &&b: @ty) -> bool { ret std::box::ptr_eq(a, b); }
fn hash_ty(&&t: @ty) -> uint { ret t.span.lo << 16u + t.span.hi; }

fn block_from_expr(e: @expr) -> blk {
let blk_ = checked_blk([], option::some::<@expr>(e), e.id);
let blk_ = default_block([], option::some::<@expr>(e), e.id);
ret {node: blk_, span: e.span};
}

fn checked_blk(stmts1: [@stmt], expr1: option::t<@expr>, id1: node_id) ->
fn default_block(stmts1: [@stmt], expr1: option::t<@expr>, id1: node_id) ->
blk_ {
ret {stmts: stmts1, expr: expr1, id: id1, rules: checked};
ret {stmts: stmts1, expr: expr1, id: id1, rules: default_blk};
}

fn obj_field_from_anon_obj_field(f: anon_obj_field) -> obj_field {
Expand Down
Loading