From f841e894437843c142ebd7e0b0a18ed00ed52457 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Thu, 25 Aug 2011 17:42:38 -0700 Subject: [PATCH] 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. --- src/comp/front/config.rs | 2 +- src/comp/front/test.rs | 13 ++++++------- src/comp/middle/typeck.rs | 7 ++++++- src/comp/syntax/ast.rs | 12 +++++++++++- src/comp/syntax/ast_util.rs | 6 +++++- src/comp/syntax/fold.rs | 3 ++- src/comp/syntax/parse/parser.rs | 26 +++++++++++++++++++------- src/comp/syntax/print/pprust.rs | 5 +++++ 8 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/comp/front/config.rs b/src/comp/front/config.rs index 4aa1c07be57c..526bf20ebb76 100644 --- a/src/comp/front/config.rs +++ b/src/comp/front/config.rs @@ -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 { diff --git a/src/comp/front/test.rs b/src/comp/front/test.rs index 8e0b71caf848..d76e26064a7f 100644 --- a/src/comp/front/test.rs +++ b/src/comp/front/test.rs @@ -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}; diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index ad199ba5cb3d..7519e2541f58 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -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) } diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index 4caddb5e25bd..d44baebb6f3d 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -82,7 +82,8 @@ tag meta_item_ { type blk = spanned; -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; tag mac_ { diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index 48bdfc47b68c..517e252dd9cc 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -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}; diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs index a91b2f31814b..770ed307e141 100644 --- a/src/comp/syntax/fold.rs +++ b/src/comp/syntax/fold.rs @@ -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_ { diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index f7329bd3b3b8..729338b4b302 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -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); } diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index 051c114002b5..52ee1eaeacc1 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -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);