integrate purity into type
This commit is contained in:
parent
22a10f0e4a
commit
653a1f8781
@ -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())
|
||||
|
@ -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" }
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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)];
|
||||
|
@ -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 {
|
||||
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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, _) {
|
||||
|
@ -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) {
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
@ -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 {}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
48
src/test/compile-fail/pure-higher-order.rs
Normal file
48
src/test/compile-fail/pure-higher-order.rs
Normal 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() {
|
||||
}
|
Loading…
Reference in New Issue
Block a user