Support unchecked blocks

This patch supports the syntax

    unchecked {
      ...
    }

    to disable purity checking within a block. Presumably it will only be
    used within a declared "pure fn". However, there is no checking that it
    doesn't occur elsewhere, and it would be harmless for it to do so.

    I went with Lindsey's suggestion for the syntax, but it's subject to
    change.

    This allows you to write code that uses predicates that call arbitrary
    Rust functions, but you must declare your intentions by wrapping it in
    an unchecked { ... } block. The test case run-pass/unchecked-predicates.rs
    demonstrates how to do that.
This commit is contained in:
Tim Chevalier 2011-08-25 17:42:38 -07:00
parent d9bc3cb10c
commit f841e89443
8 changed files with 55 additions and 19 deletions

View File

@ -77,7 +77,7 @@ fn fold_block(cfg: &ast::crate_cfg, b: &ast::blk_, fld: fold::ast_fold) ->
let filtered_stmts = vec::filter_map(filter, b.stmts);
ret {stmts: vec::map(fld.fold_stmt, filtered_stmts),
expr: option::map(fld.fold_expr, b.expr),
id: b.id};
id: b.id, rules: b.rules};
}
fn item_in_cfg(cfg: &ast::crate_cfg, item: &@ast::item) -> bool {

View File

@ -4,7 +4,8 @@ import std::option;
import std::vec;
import syntax::ast;
import syntax::ast_util;
import syntax::ast_util::dummy_sp;
import syntax::ast_util::*;
//import syntax::ast_util::dummy_sp;
import syntax::fold;
import syntax::print::pprust;
import front::attr;
@ -189,8 +190,8 @@ fn mk_tests(cx: &test_ctxt) -> @ast::item {
// The vector of test_descs for this crate
let test_descs = mk_test_desc_vec(cx);
let body_: ast::blk_ =
{stmts: [], expr: option::some(test_descs), id: cx.next_node_id()};
let body_: ast::blk_ = checked_blk([], option::some(test_descs),
cx.next_node_id());
let body = nospan(body_);
let fn_ = {decl: decl, proto: proto, body: body};
@ -305,10 +306,8 @@ fn mk_main(cx: &test_ctxt) -> @ast::item {
let test_main_call_expr = mk_test_main_call(cx);
let body_: ast::blk_ =
{stmts: [],
expr: option::some(test_main_call_expr),
id: cx.next_node_id()};
let body_: ast::blk_ = checked_blk([], 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};

View File

@ -2081,7 +2081,12 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
check_fn(fcx.ccx, f, id, some(fcx));
}
ast::expr_block(b) {
bot = check_block(fcx, 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);
let typ =
alt b.node.expr {
some(expr) { expr_ty(tcx, expr) }

View File

@ -82,7 +82,8 @@ tag meta_item_ {
type blk = spanned<blk_>;
type blk_ = {stmts: [@stmt], expr: option::t<@expr>, id: node_id};
type blk_ = {stmts: [@stmt], expr: option::t<@expr>,
id: node_id, rules: check_mode};
type pat = {id: node_id, node: pat_, span: span};
@ -223,6 +224,15 @@ tag expr_ {
expr_uniq(@expr);
}
/*
// Says whether this is a block the user marked as
// "unchecked"
tag blk_sort {
blk_unchecked; // declared as "exception to effect-checking rules"
blk_checked; // all typing rules apply
}
*/
type mac = spanned<mac_>;
tag mac_ {

View File

@ -184,10 +184,14 @@ 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_ = {stmts: [], expr: option::some::<@expr>(e), id: e.id};
let blk_ = checked_blk([], option::some::<@expr>(e), e.id);
ret {node: blk_, span: e.span};
}
fn checked_blk(stmts1: [@stmt], expr1: option::t<@expr>, id1: node_id)
-> blk_ {
ret {stmts: stmts1, expr: expr1, id: id1, rules: checked};
}
fn obj_field_from_anon_obj_field(f: &anon_obj_field) -> obj_field {
ret {mut: f.mut, ty: f.ty, ident: f.ident, id: f.id};

View File

@ -255,7 +255,8 @@ fn noop_fold_method(m: &method_, fld: ast_fold) -> method_ {
fn noop_fold_block(b: &blk_, fld: ast_fold) -> blk_ {
ret {stmts: vec::map(fld.fold_stmt, b.stmts),
expr: option::map(fld.fold_expr, b.expr),
id: b.id};
id: b.id,
rules: b.rules};
}
fn noop_fold_stmt(s: &stmt_, fld: ast_fold) -> stmt_ {

View File

@ -837,7 +837,7 @@ fn parse_bottom_expr(p: &parser) -> @ast::expr {
} else if p.peek() == token::BINOP(token::OR) {
ret parse_fn_block_expr(p);
} else {
let blk = parse_block_tail(p, lo);
let blk = parse_block_tail(p, lo, ast::checked);
ret mk_expr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk));
}
} else if eat_word(p, "if") {
@ -860,6 +860,10 @@ fn parse_bottom_expr(p: &parser) -> @ast::expr {
ret parse_fn_expr(p, ast::proto_block);
} else if eat_word(p, "lambda") {
ret parse_fn_expr(p, ast::proto_closure);
} else if eat_word(p, "unchecked") {
expect(p, token::LBRACE);
let blk = parse_block_tail(p, lo, ast::unchecked);
ret mk_expr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk));
} else if p.peek() == token::LBRACKET {
p.bump();
let mut = parse_mutability(p);
@ -876,7 +880,7 @@ fn parse_bottom_expr(p: &parser) -> @ast::expr {
ret mk_mac_expr(p, lo, p.get_hi_pos(), ast::mac_embed_type(ty))
} else if p.peek() == token::POUND_LBRACE {
p.bump();
let blk = ast::mac_embed_block(parse_block_tail(p, lo));
let blk = ast::mac_embed_block(parse_block_tail(p, lo, ast::checked));
ret mk_mac_expr(p, lo, p.get_hi_pos(), blk);
} else if p.peek() == token::ELLIPSIS {
p.bump();
@ -1309,7 +1313,7 @@ fn parse_fn_expr(p: &parser, proto: ast::proto) -> @ast::expr {
fn parse_fn_block_expr(p: &parser) -> @ast::expr {
let lo = p.get_last_lo_pos();
let decl = parse_fn_block_decl(p);
let body = parse_block_tail(p, lo);
let body = parse_block_tail(p, lo, ast::checked);
let _fn = {decl: decl, proto: ast::proto_block, body: body};
ret mk_expr(p, lo, body.span.hi, ast::expr_fn(_fn));
}
@ -1664,12 +1668,20 @@ fn stmt_ends_with_semi(stmt: &ast::stmt) -> bool {
fn parse_block(p: &parser) -> ast::blk {
let lo = p.get_lo_pos();
expect(p, token::LBRACE);
be parse_block_tail(p, lo);
if eat_word(p, "unchecked") {
be parse_block_tail(p, lo, ast::unchecked);
}
else {
expect(p, token::LBRACE);
be parse_block_tail(p, lo, ast::checked);
}
}
// Precondition: already parsed the '{' or '#{'
// I guess that also means "already parsed the 'impure'" if
// necessary, and this should take a qualifier.
// some blocks start with "#{"...
fn parse_block_tail(p: &parser, lo: uint) -> ast::blk {
fn parse_block_tail(p: &parser, lo: uint, s: ast::check_mode) -> ast::blk {
let stmts: [@ast::stmt] = [];
let expr: option::t<@ast::expr> = none;
while p.peek() != token::RBRACE {
@ -1710,7 +1722,7 @@ fn parse_block_tail(p: &parser, lo: uint) -> ast::blk {
}
let hi = p.get_hi_pos();
p.bump();
let bloc = {stmts: stmts, expr: expr, id: p.get_id()};
let bloc = {stmts: stmts, expr: expr, id: p.get_id(), rules: s};
ret spanned(lo, hi, bloc);
}

View File

@ -579,6 +579,11 @@ tag embed_type { block_macro; block_block_fn; block_normal; }
fn print_possibly_embedded_block(s: &ps, blk: &ast::blk, embedded: embed_type,
indented: uint) {
alt blk.node.rules {
ast::unchecked. { word(s.s, "unchecked"); }
_ {}
}
maybe_print_comment(s, blk.span.lo);
let ann_node = node_block(s, blk);
s.ann.pre(ann_node);