integrate purity into type

This commit is contained in:
Niko Matsakis 2012-05-24 23:44:58 -07:00
parent 22a10f0e4a
commit 653a1f8781
21 changed files with 320 additions and 141 deletions

View File

@ -136,7 +136,18 @@ class parser {
}
fn get_id() -> node_id { next_node_id(self.sess) }
fn parse_ty_fn() -> fn_decl {
fn parse_ty_fn(purity: ast::purity) -> ty_ {
let proto = if self.eat_keyword("native") {
self.expect_keyword("fn");
ast::proto_bare
} else {
self.expect_keyword("fn");
self.parse_fn_ty_proto()
};
ty_fn(proto, self.parse_ty_fn_decl(purity))
}
fn parse_ty_fn_decl(purity: ast::purity) -> fn_decl {
let inputs =
self.parse_seq(token::LPAREN, token::RPAREN,
seq_sep(token::COMMA)) { |p|
@ -159,7 +170,7 @@ class parser {
let constrs: [@constr] = [];
let (ret_style, ret_ty) = self.parse_ret_ty();
ret {inputs: inputs.node, output: ret_ty,
purity: impure_fn, cf: ret_style,
purity: purity, cf: ret_style,
constraints: constrs};
}
@ -170,7 +181,7 @@ class parser {
let pur = p.parse_fn_purity();
let ident = p.parse_method_name();
let tps = p.parse_ty_params();
let d = p.parse_ty_fn(), fhi = p.last_span.hi;
let d = p.parse_ty_fn_decl(pur), fhi = p.last_span.hi;
self.expect(token::SEMI);
{ident: ident, attrs: attrs, decl: {purity: pur with d}, tps: tps,
span: mk_sp(flo, fhi)}
@ -384,16 +395,15 @@ class parser {
let region = self.parse_region_dot();
let mt = self.parse_mt();
ty_rptr(region, mt)
} else if self.eat_keyword("fn") {
let proto = self.parse_fn_ty_proto();
alt proto {
proto_bare { self.warn("fn is deprecated, use native fn"); }
_ { /* fallthrough */ }
}
ty_fn(proto, self.parse_ty_fn())
} else if self.eat_keyword("pure") {
self.parse_ty_fn(ast::pure_fn)
} else if self.eat_keyword("unsafe") {
self.parse_ty_fn(ast::unsafe_fn)
} else if self.is_keyword("fn") {
self.parse_ty_fn(ast::impure_fn)
} else if self.eat_keyword("native") {
self.expect_keyword("fn");
ty_fn(proto_bare, self.parse_ty_fn())
ty_fn(proto_bare, self.parse_ty_fn_decl(ast::impure_fn))
} else if self.token == token::MOD_SEP || is_ident(self.token) {
let path = self.parse_path_with_tps(colons_before_params);
ty_path(path, self.get_id())

View File

@ -1029,6 +1029,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
cbox(s, indent_unit);
// head-box, will be closed by print-block at start
ibox(s, 0u);
print_purity(s, decl.purity);
word(s.s, proto_to_str(proto));
print_fn_args_and_ret(s, decl, *cap_clause);
space(s.s);
@ -1322,10 +1323,8 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
fn print_fn(s: ps, decl: ast::fn_decl, name: ast::ident,
typarams: [ast::ty_param]) {
alt decl.purity {
ast::impure_fn { head(s, "fn"); }
ast::unsafe_fn { head(s, "unsafe fn"); }
ast::pure_fn { head(s, "pure fn"); }
ast::crust_fn { head(s, "crust fn"); }
ast::impure_fn { head(s, "fn") }
_ { head(s, purity_to_str(decl.purity) + " fn") }
}
word(s.s, name);
print_type_params(s, typarams);
@ -1825,6 +1824,22 @@ fn opt_proto_to_str(opt_p: option<ast::proto>) -> str {
}
}
fn purity_to_str(p: ast::purity) -> str {
alt p {
ast::impure_fn {"impure"}
ast::unsafe_fn {"unsafe"}
ast::pure_fn {"pure"}
ast::crust_fn {"crust"}
}
}
fn print_purity(s: ps, p: ast::purity) {
alt p {
ast::impure_fn {}
_ { word_nbsp(s, purity_to_str(p)) }
}
}
fn proto_to_str(p: ast::proto) -> str {
ret alt p {
ast::proto_bare { "native fn" }

View File

@ -159,8 +159,8 @@ fn parse_constr<T: copy>(st: @pstate, conv: conv_did,
ret @respan(sp, {path: pth, args: args, id: def});
}
fn parse_ty_rust_fn(st: @pstate, conv: conv_did, p: ast::proto) -> ty::t {
ret ty::mk_fn(st.tcx, {proto: p with parse_ty_fn(st, conv)});
fn parse_ty_rust_fn(st: @pstate, conv: conv_did) -> ty::t {
ret ty::mk_fn(st.tcx, parse_ty_fn(st, conv));
}
fn parse_proto(c: char) -> ast::proto {
@ -335,8 +335,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
ret ty::mk_tup(st.tcx, params);
}
'f' {
let proto = parse_proto(next(st));
parse_ty_rust_fn(st, conv, proto)
parse_ty_rust_fn(st, conv)
}
'r' {
assert next(st) == '[';
@ -441,7 +440,18 @@ fn parse_hex(st: @pstate) -> uint {
};
}
fn parse_purity(c: char) -> purity {
alt check c {
'u' {unsafe_fn}
'p' {pure_fn}
'i' {impure_fn}
'c' {crust_fn}
}
}
fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::fn_ty {
let proto = parse_proto(next(st));
let purity = parse_purity(next(st));
assert (next(st) == '[');
let mut inputs: [ty::arg] = [];
while peek(st) != ']' {
@ -458,7 +468,7 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::fn_ty {
st.pos += 1u; // eat the ']'
let cs = parse_constrs(st, conv);
let (ret_style, ret_ty) = parse_ret_ty(st, conv);
ret {proto: ast::proto_bare, inputs: inputs, output: ret_ty,
ret {purity: purity, proto: proto, inputs: inputs, output: ret_ty,
ret_style: ret_style, constraints: cs};
}

View File

@ -262,7 +262,6 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
w.write_char(']');
}
ty::ty_fn(f) {
enc_proto(w, f.proto);
enc_ty_fn(w, cx, f);
}
ty::ty_res(def, ty, substs) {
@ -331,7 +330,18 @@ fn enc_mode(w: io::writer, cx: @ctxt, m: mode) {
}
}
fn enc_purity(w: io::writer, p: purity) {
alt p {
pure_fn { w.write_char('p'); }
impure_fn { w.write_char('i'); }
unsafe_fn { w.write_char('u'); }
crust_fn { w.write_char('c'); }
}
}
fn enc_ty_fn(w: io::writer, cx: @ctxt, ft: ty::fn_ty) {
enc_proto(w, ft.proto);
enc_purity(w, ft.purity);
w.write_char('[');
for ft.inputs.each {|arg|
enc_mode(w, cx, arg.mode);

View File

@ -13,6 +13,7 @@ import result::{result, ok, err, extensions};
import syntax::print::pprust;
import util::common::indenter;
import ast_util::op_expr_callee_id;
import ty::to_str;
export check_crate, root_map, mutbl_map;
@ -504,14 +505,14 @@ enum check_loan_ctxt = @{
// allow mutating immutable fields in the same class if
// we are in a ctor, we track the self id
mut in_ctor: bool,
mut declared_purity: ast::purity
mut declared_purity: ast::purity,
mut fn_args: [ast::node_id]
};
// if we are enforcing purity, why are we doing so?
enum purity_cause {
// enforcing purity because fn was declared pure:
pc_declaration,
pc_pure_fn,
// enforce purity because we need to guarantee the
// validity of some alias; `bckerr` describes the
@ -526,7 +527,8 @@ fn check_loans(bccx: borrowck_ctxt,
req_maps: req_maps,
reported: int_hash(),
mut in_ctor: false,
mut declared_purity: ast::impure_fn});
mut declared_purity: ast::impure_fn,
mut fn_args: []});
let vt = visit::mk_vt(@{visit_expr: check_loans_in_expr,
visit_block: check_loans_in_block,
visit_fn: check_loans_in_fn
@ -570,7 +572,7 @@ impl methods for check_loan_ctxt {
// otherwise, remember what was declared as the
// default, but we must scan for requirements
// imposed by the borrow check
ast::pure_fn { some(pc_declaration) }
ast::pure_fn { some(pc_pure_fn) }
ast::crust_fn | ast::impure_fn { none }
};
@ -627,70 +629,79 @@ impl methods for check_loan_ctxt {
// when we are in a pure context, we check each call to ensure
// that the function which is invoked is itself pure.
fn check_pure(pc: purity_cause, expr: @ast::expr) {
fn check_pure_callee_or_arg(pc: purity_cause, expr: @ast::expr) {
let tcx = self.tcx();
alt ty::get(tcx.ty(expr)).struct {
ty::ty_fn(_) {
// Extract purity or unsafety based on what kind of callee
// we've got. This would be cleaner if we just admitted
// that we have an effect system and carried the purity
// etc around in the type.
// First, check the def_map---if expr.id is present then
// expr must be a path (at least I think that's the idea---NDM)
let callee_purity = alt tcx.def_map.find(expr.id) {
some(ast::def_fn(_, p)) { p }
some(ast::def_variant(_, _)) { ast::pure_fn }
_ {
// otherwise it may be a method call that we can trace
// to the def'n site:
alt self.bccx.method_map.find(expr.id) {
some(typeck::method_static(did)) {
if did.crate == ast::local_crate {
alt tcx.items.get(did.node) {
ast_map::node_method(m, _, _) { m.decl.purity }
_ { tcx.sess.span_bug(expr.span,
"Node not bound \
to a method") }
}
} else {
metadata::csearch::lookup_method_purity(
tcx.sess.cstore,
did)
}
}
some(typeck::method_param(iid, n_m, _, _)) |
some(typeck::method_iface(iid, n_m)) {
ty::iface_methods(tcx, iid)[n_m].purity
}
none {
// otherwise it's just some dang thing. We know
// it cannot be unsafe because we do not allow
// unsafe functions to be used as values (or,
// rather, we only allow that inside an unsafe
// block, and then it's up to the user to keep
// things confined).
ast::impure_fn
}
}
}
};
#debug["check_pure_callee_or_arg(pc=%?, expr=%s, ty=%s)",
pc, pprust::expr_to_str(expr),
ty_to_str(self.tcx(), tcx.ty(expr))];
alt callee_purity {
ast::crust_fn | ast::pure_fn {
/*ok*/
}
ast::impure_fn | ast::unsafe_fn {
// Purity rules: an expr B is a legal callee or argument to a
// call within a pure function A if at least one of the
// following holds:
//
// (a) A was declared pure and B is one of its arguments;
// (b) B is a stack closure;
// (c) B is a pure fn;
// (d) B is not a fn.
alt expr.node {
ast::expr_path(_) if pc == pc_pure_fn {
let def = self.tcx().def_map.get(expr.id);
let did = ast_util::def_id_of_def(def);
let is_fn_arg =
did.crate == ast::local_crate &&
self.fn_args.contains(did.node);
if is_fn_arg { ret; } // case (a) above
}
ast::expr_fn_block(*) | ast::expr_fn(*) {
if self.is_stack_closure(expr.id) { ret; } // case (b) above
}
_ {}
}
let expr_ty = tcx.ty(expr);
alt ty::get(expr_ty).struct {
ty::ty_fn(fn_ty) {
alt fn_ty.purity {
ast::pure_fn { ret; } // case (c) above
ast::impure_fn | ast::unsafe_fn | ast::crust_fn {
self.report_purity_error(
pc, expr.span,
"access to non-pure functions");
#fmt["access to %s function",
pprust::purity_to_str(fn_ty.purity)]);
}
}
}
_ { /* not a fn, ok */ }
_ { ret; } // case (d) above
}
}
// True if the expression with the given `id` is a stack closure.
// The expression must be an expr_fn(*) or expr_fn_block(*)
fn is_stack_closure(id: ast::node_id) -> bool {
let fn_ty = ty::node_id_to_type(self.tcx(), id);
let proto = ty::ty_fn_proto(fn_ty);
alt proto {
ast::proto_block | ast::proto_any {true}
ast::proto_bare | ast::proto_uniq | ast::proto_box {false}
}
}
fn is_allowed_pure_arg(expr: @ast::expr) -> bool {
ret alt expr.node {
ast::expr_path(_) {
let def = self.tcx().def_map.get(expr.id);
let did = ast_util::def_id_of_def(def);
did.crate == ast::local_crate && self.fn_args.contains(did.node)
}
ast::expr_fn_block(*) | ast::expr_fn(*) {
self.is_stack_closure(expr.id)
}
_ {false}
};
}
fn check_for_conflicting_loans(scope_id: ast::node_id) {
let new_loanss = alt self.req_maps.req_loan_map.find(scope_id) {
none { ret; }
@ -814,7 +825,7 @@ impl methods for check_loan_ctxt {
fn report_purity_error(pc: purity_cause, sp: span, msg: str) {
alt pc {
pc_declaration {
pc_pure_fn {
self.tcx().sess.span_err(
sp,
#fmt["%s prohibited in pure context", msg]);
@ -888,23 +899,37 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
#debug["purity on entry=%?", self.declared_purity];
save_and_restore(self.in_ctor) {||
save_and_restore(self.declared_purity) {||
// In principle, we could consider fk_anon(*) or
// fk_fn_block(*) to be in a ctor, I suppose, but the
// purpose of the in_ctor flag is to allow modifications
// of otherwise immutable fields and typestate wouldn't be
// able to "see" into those functions anyway, so it
// wouldn't be very helpful.
alt fk {
visit::fk_ctor(*) { self.in_ctor = true; }
_ { self.in_ctor = false; }
};
save_and_restore(self.fn_args) {||
let is_stack_closure = self.is_stack_closure(id);
// NDM this doesn't seem algother right, what about fn items
// nested in pure fns? etc?
// In principle, we could consider fk_anon(*) or
// fk_fn_block(*) to be in a ctor, I suppose, but the
// purpose of the in_ctor flag is to allow modifications
// of otherwise immutable fields and typestate wouldn't be
// able to "see" into those functions anyway, so it
// wouldn't be very helpful.
alt fk {
visit::fk_ctor(*) {
self.in_ctor = true;
self.declared_purity = decl.purity;
self.fn_args = decl.inputs.map({|i| i.id});
}
visit::fk_anon(*) |
visit::fk_fn_block(*) if is_stack_closure {
self.in_ctor = false;
// inherits the purity/fn_args from enclosing ctxt
}
visit::fk_anon(*) | visit::fk_fn_block(*) |
visit::fk_method(*) | visit::fk_item_fn(*) |
visit::fk_res(*) | visit::fk_dtor(*) {
self.in_ctor = false;
self.declared_purity = decl.purity;
self.fn_args = decl.inputs.map({|i| i.id});
}
}
self.declared_purity = decl.purity;
visit::visit_fn(fk, decl, body, sp, id, self, visitor);
visit::visit_fn(fk, decl, body, sp, id, self, visitor);
}
}
}
#debug["purity on exit=%?", self.declared_purity];
@ -960,8 +985,8 @@ fn check_loans_in_expr(expr: @ast::expr,
alt self.purity(expr.id) {
none {}
some(pc) {
self.check_pure(pc, f);
for args.each { |arg| self.check_pure(pc, arg) }
self.check_pure_callee_or_arg(pc, f);
for args.each { |arg| self.check_pure_callee_or_arg(pc, arg) }
}
}
let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));

View File

@ -791,7 +791,8 @@ fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id, substs: [ty::t])
} else if did.crate == ast::local_crate {
get_item_val(ccx, did.node)
} else {
let fty = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare,
let fty = ty::mk_fn(ccx.tcx, {purity: ast::impure_fn,
proto: ast::proto_bare,
inputs: [{mode: ast::expl(ast::by_ref),
ty: ty::mk_nil_ptr(ccx.tcx)}],
output: ty::mk_nil(ccx.tcx),
@ -1949,12 +1950,14 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> option<ty::t> {
// FIXME[mono] could do this recursively. is that worthwhile?
alt ty::get(ty).struct {
ty::ty_box(mt) { some(ty::mk_opaque_box(tcx)) }
ty::ty_fn(fty) { some(ty::mk_fn(tcx, {proto: fty.proto,
ty::ty_fn(fty) { some(ty::mk_fn(tcx, {purity: ast::impure_fn,
proto: fty.proto,
inputs: [],
output: ty::mk_nil(tcx),
ret_style: ast::return_val,
constraints: []})) }
ty::ty_iface(_, _) { some(ty::mk_fn(tcx, {proto: ast::proto_box,
ty::ty_iface(_, _) { some(ty::mk_fn(tcx, {purity: ast::impure_fn,
proto: ast::proto_box,
inputs: [],
output: ty::mk_nil(tcx),
ret_style: ast::return_val,

View File

@ -157,6 +157,7 @@ export atttce_unresolved, atttce_resolved;
export mach_sty;
export ty_sort_str;
export normalize_ty;
export to_str;
// Data types
@ -290,7 +291,8 @@ enum closure_kind {
ck_uniq,
}
type fn_ty = {proto: ast::proto,
type fn_ty = {purity: ast::purity,
proto: ast::proto,
inputs: [arg],
output: t,
ret_style: ret_style,
@ -378,6 +380,7 @@ enum terr_vstore_kind {
enum type_err {
terr_mismatch,
terr_ret_style_mismatch(ast::ret_style, ast::ret_style),
terr_purity_mismatch(purity, purity),
terr_mutability,
terr_proto_mismatch(ast::proto, ast::proto),
terr_box_mutability,
@ -425,6 +428,12 @@ impl of vid for region_vid {
fn to_str() -> str { #fmt["<R%u>", self.to_uint()] }
}
impl of to_str::to_str for purity {
fn to_str() -> str {
purity_to_str(self)
}
}
fn param_bounds_to_kind(bounds: param_bounds) -> kind {
let mut kind = kind_noncopyable();
for vec::each(*bounds) {|bound|
@ -2375,6 +2384,9 @@ fn type_err_to_str(cx: ctxt, err: type_err) -> str {
ret to_str(actual) + " function found where " + to_str(expect) +
" function was expected";
}
terr_purity_mismatch(f1, f2) {
ret #fmt["expected %s fn but found %s fn", f1.to_str(), f2.to_str()];
}
terr_proto_mismatch(e, a) {
ret #fmt["closure protocol mismatch (%s vs %s)",
proto_to_str(e), proto_to_str(a)];

View File

@ -197,8 +197,8 @@ fn check_main_fn_ty(ccx: @crate_ctxt,
let tcx = ccx.tcx;
let main_t = ty::node_id_to_type(tcx, main_id);
alt ty::get(main_t).struct {
ty::ty_fn({proto: ast::proto_bare, inputs, output,
ret_style: ast::return_val, constraints}) {
ty::ty_fn({purity: ast::impure_fn, proto: ast::proto_bare,
inputs, output, ret_style: ast::return_val, constraints}) {
alt tcx.items.find(main_id) {
some(ast_map::node_item(it,_)) {
alt it.node {

View File

@ -429,7 +429,8 @@ fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>(
let out_constrs = vec::map(decl.constraints) {|constr|
ty::ast_constr_to_constr(self.tcx(), constr)
};
{proto: proto, inputs: input_tys,
{purity: decl.purity, proto: proto, inputs: input_tys,
output: output_ty, ret_style: decl.cf, constraints: out_constrs}
}
}

View File

@ -1379,7 +1379,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
i += 1u;
}
let ft = ty::mk_fn(tcx, {proto: proto,
let ft = ty::mk_fn(tcx, {purity: ast::impure_fn, proto: proto,
inputs: out_args, output: rt,
ret_style: cf, constraints: constrs});
fcx.write_ty(id, ft);
@ -1625,7 +1625,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let ty_nilp = ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx),
mutbl: ast::m_imm});
let m = ast::expl(ty::default_arg_mode_for_ty(ty_uint));
ty::mk_fn(tcx, {proto: ast::proto_any,
ty::mk_fn(tcx, {purity: ast::impure_fn,
proto: ast::proto_any,
inputs: [{mode: m, ty: ty_uint},
{mode: m, ty: ty_uint}],
output: ty_nilp,

View File

@ -113,7 +113,8 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
let arg_ty = ccx.to_ty(rs, va.ty);
{mode: ast::expl(ast::by_copy), ty: arg_ty}
};
ty::mk_fn(tcx, {proto: ast::proto_box,
ty::mk_fn(tcx, {purity: ast::pure_fn,
proto: ast::proto_box,
inputs: args,
output: enum_ty,
ret_style: ast::return_val,
@ -344,12 +345,14 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs);
let t_ctor = ty::mk_fn(tcx, {
purity: ast::pure_fn,
proto: ast::proto_box,
inputs: [{mode: ast::expl(ast::by_copy), ty: t_arg.ty}],
output: t_res,
ret_style: ast::return_val, constraints: []
});
let t_dtor = ty::mk_fn(tcx, {
purity: ast::impure_fn,
proto: ast::proto_box,
inputs: [t_arg], output: ty::mk_nil(tcx),
ret_style: ast::return_val, constraints: []
@ -521,7 +524,8 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::native_item) {
ret;
}
};
let fty = ty::mk_fn(tcx, {proto: ast::proto_bare,
let fty = ty::mk_fn(tcx, {purity: ast::impure_fn,
proto: ast::proto_bare,
inputs: inputs, output: output,
ret_style: ast::return_val,
constraints: []});
@ -594,6 +598,8 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
let tpt = {bounds: bounds,
rp: ast::rp_none, // functions do not have a self
ty: ty::mk_fn(ccx.tcx, tofd)};
#debug["type of %s (id %d) is %s",
it.ident, it.id, ty_to_str(tcx, tpt.ty)];
ccx.tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
@ -715,7 +721,8 @@ fn ty_of_native_fn_decl(ccx: @crate_ctxt,
let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) };
let output_ty = ast_ty_to_ty(ccx, rb, decl.output);
let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare,
let t_fn = ty::mk_fn(ccx.tcx, {purity: decl.purity,
proto: ast::proto_bare,
inputs: input_tys,
output: output_ty,
ret_style: ast::return_val,

View File

@ -149,13 +149,14 @@ import std::map::hashmap;
import middle::ty;
import middle::ty::{ty_vid, tys_in_fn_ty, region_vid, vid};
import syntax::{ast, ast_util};
import syntax::ast::{ret_style};
import syntax::ast::{ret_style, purity};
import util::ppaux::{ty_to_str, mt_to_str};
import result::{result, extensions, ok, err, map_vec, map_vec2, iter_vec2};
import ty::{mk_fn, type_is_bot};
import check::regionmanip::{replace_bound_regions_in_fn_ty};
import driver::session::session;
import util::common::{indent, indenter};
import ast::{unsafe_fn, impure_fn, pure_fn, crust_fn};
export infer_ctxt;
export new_infer_ctxt;
@ -1179,6 +1180,7 @@ iface combine {
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg>;
fn protos(p1: ast::proto, p2: ast::proto) -> cres<ast::proto>;
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
fn purities(f1: purity, f2: purity) -> cres<purity>;
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region>;
fn regions(a: ty::region, b: ty::region) -> cres<ty::region>;
fn vstores(vk: ty::terr_vstore_kind,
@ -1342,14 +1344,17 @@ fn super_fns<C:combine>(
self.ret_styles(a_f.ret_style, b_f.ret_style).chain {|rs|
argvecs(self, a_f.inputs, b_f.inputs).chain {|inputs|
self.tys(a_f.output, b_f.output).chain {|output|
self.purities(a_f.purity, b_f.purity).chain {|purity|
//FIXME self.infcx().constrvecs(a_f.constraints,
//FIXME b_f.constraints).then {||
ok({proto: p,
ok({purity: purity,
proto: p,
inputs: inputs,
output: output,
ret_style: rs,
constraints: a_f.constraints})
//FIXME }
}
}
}
}
@ -1579,6 +1584,12 @@ impl of combine for sub {
}
}
fn purities(f1: purity, f2: purity) -> cres<purity> {
self.lub().purities(f1, f2).compare(f2) {||
ty::terr_purity_mismatch(f2, f1)
}
}
fn ret_styles(a: ret_style, b: ret_style) -> cres<ret_style> {
self.lub().ret_styles(a, b).compare(b) {||
ty::terr_ret_style_mismatch(b, a)
@ -1739,6 +1750,15 @@ impl of combine for lub {
}
}
fn purities(f1: purity, f2: purity) -> cres<purity> {
alt (f1, f2) {
(unsafe_fn, _) | (_, unsafe_fn) {ok(unsafe_fn)}
(impure_fn, _) | (_, impure_fn) {ok(impure_fn)}
(crust_fn, _) | (_, crust_fn) {ok(crust_fn)}
(pure_fn, pure_fn) {ok(pure_fn)}
}
}
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
alt (r1, r2) {
(ast::return_val, _) |
@ -1931,6 +1951,15 @@ impl of combine for glb {
}
}
fn purities(f1: purity, f2: purity) -> cres<purity> {
alt (f1, f2) {
(pure_fn, _) | (_, pure_fn) {ok(pure_fn)}
(crust_fn, _) | (_, crust_fn) {ok(crust_fn)}
(impure_fn, _) | (_, impure_fn) {ok(impure_fn)}
(unsafe_fn, unsafe_fn) {ok(unsafe_fn)}
}
}
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
alt (r1, r2) {
(ast::return_val, ast::return_val) {

View File

@ -5,7 +5,7 @@ import metadata::encoder;
import syntax::codemap;
import syntax::print::pprust;
import syntax::print::pprust::{path_to_str, constr_args_to_str, proto_to_str,
mode_to_str};
mode_to_str, purity_to_str};
import syntax::{ast, ast_util};
import syntax::ast_map;
import driver::session::session;
@ -107,10 +107,17 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
};
modestr + ty_to_str(cx, ty)
}
fn fn_to_str(cx: ctxt, proto: ast::proto, ident: option<ast::ident>,
fn fn_to_str(cx: ctxt, purity: ast::purity, proto: ast::proto,
ident: option<ast::ident>,
inputs: [arg], output: t, cf: ast::ret_style,
constrs: [@constr]) -> str {
let mut s = proto_to_str(proto);
let mut s;
s = alt purity {
ast::impure_fn {""}
_ {purity_to_str(purity) + " "}
};
s += proto_to_str(proto);
alt ident { some(i) { s += " "; s += i; } _ { } }
s += "(";
let mut strs = [];
@ -128,8 +135,9 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
ret s;
}
fn method_to_str(cx: ctxt, m: method) -> str {
ret fn_to_str(cx, m.fty.proto, some(m.ident), m.fty.inputs,
m.fty.output, m.fty.ret_style, m.fty.constraints) + ";";
ret fn_to_str(
cx, m.fty.purity, m.fty.proto, some(m.ident), m.fty.inputs,
m.fty.output, m.fty.ret_style, m.fty.constraints) + ";";
}
fn field_to_str(cx: ctxt, f: field) -> str {
ret f.ident + ": " + mt_to_str(cx, f.mt);
@ -178,8 +186,8 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
"(" + str::connect(strs, ",") + ")"
}
ty_fn(f) {
fn_to_str(cx, f.proto, none, f.inputs, f.output, f.ret_style,
f.constraints)
fn_to_str(cx, f.purity, f.proto, none, f.inputs,
f.output, f.ret_style, f.constraints)
}
ty_var(v) { v.to_str() }
ty_param(id, _) {

View File

@ -9,7 +9,7 @@ fn borrow_from_arg_imm_ref(&&v: ~int) {
fn borrow_from_arg_mut_ref(&v: ~int) {
borrow(v); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn borrow_from_arg_move(-v: ~int) {

View File

@ -31,7 +31,7 @@ fn process(_i: int) {}
fn match_const_box_and_do_bad_things(v: &const @const option<int>) {
alt *v {
@some(i) { //! ERROR illegal borrow unless pure: enum variant in aliasable, mutable location
process(i) //! NOTE impure due to access to non-pure functions
process(i) //! NOTE impure due to access to impure function
}
@none {}
}

View File

@ -37,7 +37,7 @@ fn match_const_reg_unused(v: &const option<int>) {
fn match_const_reg_impure(v: &const option<int>) {
alt *v {
some(i) {impure(i)} //! ERROR illegal borrow unless pure: enum variant in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
none {}
}
}

View File

@ -9,7 +9,7 @@ fn foo(v: &const option<int>) {
some(i) {
//!^ ERROR illegal borrow unless pure: enum variant in aliasable, mutable location
unchecked {
impure(i); //! NOTE impure due to access to non-pure functions
impure(i); //! NOTE impure due to access to impure function
}
}
none {

View File

@ -5,22 +5,22 @@ fn borrow(_v: &int) {}
fn box_mut(v: @mut ~int) {
borrow(*v); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_rec_mut(v: @{mut f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_mut_rec(v: @mut {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_mut_recs(v: @mut {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_imm(v: @~int) {
@ -37,27 +37,27 @@ fn box_imm_recs(v: @{f: {g: {h: ~int}}}) {
fn box_const(v: @const ~int) {
borrow(*v); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_rec_const(v: @{const f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_recs_const(v: @{f: {g: {const h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_const_rec(v: @const {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_const_recs(v: @const {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn main() {

View File

@ -4,22 +4,22 @@ fn borrow(_v: &int) {}
fn box_mut(v: &mut ~int) {
borrow(*v); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_rec_mut(v: &{mut f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_mut_rec(v: &mut {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_mut_recs(v: &mut {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_imm(v: &~int) {
@ -36,27 +36,27 @@ fn box_imm_recs(v: &{f: {g: {h: ~int}}}) {
fn box_const(v: &const ~int) {
borrow(*v); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_rec_const(v: &{const f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_recs_const(v: &{f: {g: {const h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_const_rec(v: &const {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn box_const_recs(v: &const {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
//!^ NOTE impure due to access to non-pure functions
//!^ NOTE impure due to access to impure function
}
fn main() {

View File

@ -3,7 +3,7 @@
fn g() { }
pure fn f(_q: int) -> bool {
g(); //! ERROR access to non-pure functions prohibited in pure context
g(); //! ERROR access to impure function prohibited in pure context
ret true;
}

View File

@ -0,0 +1,48 @@
// Test rules governing higher-order pure fns.
pure fn range(from: uint, to: uint, f: fn(uint)) {
let mut i = from;
while i < to {
f(i); // Note: legal to call argument, even if it is not pure.
i += 1u;
}
}
pure fn range2(from: uint, to: uint, f: fn(uint)) {
range(from, to) { |i|
f(i*2u);
}
}
pure fn range3(from: uint, to: uint, f: fn(uint)) {
range(from, to, f)
}
pure fn range4(from: uint, to: uint) {
range(from, to, print) //! ERROR access to impure function prohibited in pure context
}
pure fn range5(from: uint, to: uint, x: {f: fn(uint)}) {
range(from, to, x.f) //! ERROR access to impure function prohibited in pure context
}
pure fn range6(from: uint, to: uint, x: @{f: fn(uint)}) {
range(from, to, x.f) //! ERROR access to impure function prohibited in pure context
}
pure fn range7(from: uint, to: uint) {
range(from, to) { |i|
print(i); //! ERROR access to impure function prohibited in pure context
}
}
pure fn range8(from: uint, to: uint) {
range(from, to, noop);
}
fn print(i: uint) { #error["i=%u", i]; }
pure fn noop(_i: uint) {}
fn main() {
}