diff options
| author | Marijn Haverbeke <marijnh@gmail.com> | 2012-02-13 18:56:09 +0100 |
|---|---|---|
| committer | Marijn Haverbeke <marijnh@gmail.com> | 2012-02-13 21:08:05 +0100 |
| commit | 6c9d95a9a0f82895559dc1dae6300ee14c304d7e (patch) | |
| tree | 0459bf59264521a7168ccf4266ca0859f45d8a99 | |
| parent | 9caca02dac6bb9318aaa2f9ac52868b8ccead3e0 (diff) | |
| download | rust-6c9d95a9a0f82895559dc1dae6300ee14c304d7e.tar.gz rust-6c9d95a9a0f82895559dc1dae6300ee14c304d7e.zip | |
Track purity/unsafety of iface and impl methods
Closes #1807
| -rw-r--r-- | src/comp/metadata/csearch.rs | 10 | ||||
| -rw-r--r-- | src/comp/metadata/decoder.rs | 7 | ||||
| -rw-r--r-- | src/comp/metadata/encoder.rs | 17 | ||||
| -rw-r--r-- | src/comp/middle/trans/impl.rs | 14 | ||||
| -rw-r--r-- | src/comp/middle/ty.rs | 5 | ||||
| -rw-r--r-- | src/comp/middle/typeck.rs | 65 | ||||
| -rw-r--r-- | src/comp/syntax/parse/parser.rs | 33 |
7 files changed, 90 insertions, 61 deletions
diff --git a/src/comp/metadata/csearch.rs b/src/comp/metadata/csearch.rs index 6cd78d0b5c4..f77a50bf056 100644 --- a/src/comp/metadata/csearch.rs +++ b/src/comp/metadata/csearch.rs @@ -9,6 +9,7 @@ import driver::session; export get_symbol; export get_type_param_count; export lookup_defs; +export lookup_method_purity; export get_enum_variants; export get_impls_for_mod; export get_iface_methods; @@ -35,6 +36,15 @@ fn lookup_defs(cstore: cstore::cstore, cnum: ast::crate_num, ret result; } +fn lookup_method_purity(cstore: cstore::cstore, did: ast::def_id) + -> ast::purity { + let cdata = cstore::get_crate_data(cstore, did.crate).data; + alt decoder::lookup_def(did.crate, cdata, did) { + ast::def_fn(_, p) { p } + _ { fail; } + } +} + fn resolve_path(cstore: cstore::cstore, cnum: ast::crate_num, path: [ast::ident]) -> [(ast::crate_num, @[u8], ast::def_id)] { diff --git a/src/comp/metadata/decoder.rs b/src/comp/metadata/decoder.rs index 95f1589d5d9..1019a179050 100644 --- a/src/comp/metadata/decoder.rs +++ b/src/comp/metadata/decoder.rs @@ -337,7 +337,12 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) let fty = alt ty::get(ty).struct { ty::ty_fn(f) { f } _ { tcx.sess.bug("get_iface_methods: id has non-function type"); } }; - result += [{ident: name, tps: bounds, fty: fty}]; + result += [{ident: name, tps: bounds, fty: fty, + purity: alt item_family(mth) as char { + 'u' { ast::unsafe_fn } + 'f' { ast::impure_fn } + 'p' { ast::pure_fn } + }}]; } @result } diff --git a/src/comp/metadata/encoder.rs b/src/comp/metadata/encoder.rs index 793710b0b74..1226fd56aec 100644 --- a/src/comp/metadata/encoder.rs +++ b/src/comp/metadata/encoder.rs @@ -311,6 +311,10 @@ fn encode_info_for_mod(ecx: @encode_ctxt, ebml_w: ebml::writer, md: _mod, ebml::end_tag(ebml_w); } +fn purity_fn_family(p: purity) -> char { + alt p { unsafe_fn { 'u' } pure_fn { 'p' } impure_fn { 'f' } } +} + fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, &index: [entry<int>], path: ast_map::path) { let tcx = ecx.ccx.tcx; @@ -327,12 +331,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, item_fn(decl, tps, _) { ebml::start_tag(ebml_w, tag_items_data_item); encode_def_id(ebml_w, local_def(item.id)); - encode_family(ebml_w, - alt decl.purity { - unsafe_fn { 'u' } - pure_fn { 'p' } - impure_fn { 'f' } - } as u8); + encode_family(ebml_w, purity_fn_family(decl.purity) as u8); encode_type_param_bounds(ebml_w, ecx, tps); encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); encode_symbol(ecx, ebml_w, item.id); @@ -431,10 +430,9 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, index += [{val: m.id, pos: ebml_w.writer.tell()}]; ebml::start_tag(ebml_w, tag_items_data_item); encode_def_id(ebml_w, local_def(m.id)); - encode_family(ebml_w, 'f' as u8); + encode_family(ebml_w, purity_fn_family(m.decl.purity) as u8); encode_type_param_bounds(ebml_w, ecx, tps + m.tps); - encode_type(ecx, ebml_w, - node_id_to_type(tcx, m.id)); + encode_type(ecx, ebml_w, node_id_to_type(tcx, m.id)); encode_name(ebml_w, m.ident); encode_symbol(ecx, ebml_w, m.id); encode_path(ebml_w, impl_path, ast_map::path_name(m.ident)); @@ -454,6 +452,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_name(ebml_w, mty.ident); encode_type_param_bounds(ebml_w, ecx, ms[i].tps); encode_type(ecx, ebml_w, ty::mk_fn(tcx, mty.fty)); + encode_family(ebml_w, purity_fn_family(mty.purity) as u8); ebml::end_tag(ebml_w); i += 1u; } diff --git a/src/comp/middle/trans/impl.rs b/src/comp/middle/trans/impl.rs index b72a1e47a60..be751de7d68 100644 --- a/src/comp/middle/trans/impl.rs +++ b/src/comp/middle/trans/impl.rs @@ -88,8 +88,8 @@ fn trans_method_callee(bcx: @block_ctxt, callee_id: ast::node_id, } } } - typeck::method_iface(off) { - trans_iface_callee(bcx, callee_id, self, off) + typeck::method_iface(iid, off) { + trans_iface_callee(bcx, callee_id, self, iid, off) } } } @@ -165,8 +165,8 @@ fn trans_monomorphized_callee(bcx: @block_ctxt, callee_id: ast::node_id, ast_util::local_def(mth.id), some((tys, sub_origins))); } - typeck::dict_iface(_) { - ret trans_iface_callee(bcx, callee_id, base, n_method); + typeck::dict_iface(iid) { + ret trans_iface_callee(bcx, callee_id, base, iid, n_method); } typeck::dict_param(n_param, n_bound) { fail "dict_param left in monomorphized function's dict substs"; @@ -186,7 +186,7 @@ fn trans_param_callee(bcx: @block_ctxt, callee_id: ast::node_id, // Method callee where the dict comes from a boxed iface fn trans_iface_callee(bcx: @block_ctxt, callee_id: ast::node_id, - base: @ast::expr, n_method: uint) + base: @ast::expr, iface_id: ast::def_id, n_method: uint) -> lval_maybe_callee { let {bcx, val} = trans_temp_expr(bcx, base); let dict = Load(bcx, PointerCast(bcx, GEPi(bcx, val, [0, 0]), @@ -195,10 +195,6 @@ fn trans_iface_callee(bcx: @block_ctxt, callee_id: ast::node_id, // FIXME[impl] I doubt this is alignment-safe let self = PointerCast(bcx, GEPi(bcx, box, [0, abi::box_field_body]), T_opaque_cbox_ptr(bcx_ccx(bcx))); - let iface_id = alt ty::get(expr_ty(bcx, base)).struct { - ty::ty_iface(did, _) { did } - _ { fail "base has non-iface type in trans_iface_callee"; } - }; trans_vtable_callee(bcx, self, dict, callee_id, iface_id, n_method) } diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 1f7709b1fc8..e5f8fa26ee7 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -140,7 +140,10 @@ type field = {ident: ast::ident, mt: mt}; type param_bounds = @[param_bound]; -type method = {ident: ast::ident, tps: @[param_bounds], fty: fn_ty}; +type method = {ident: ast::ident, + tps: @[param_bounds], + fty: fn_ty, + purity: ast::purity}; type constr_table = hashmap<ast::node_id, [constr]>; diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index bcfb50f287e..7da03561ea0 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -25,7 +25,7 @@ enum method_origin { method_static(ast::def_id), // iface id, method num, param num, bound num method_param(ast::def_id, uint, uint, uint), - method_iface(uint), + method_iface(ast::def_id, uint), } type method_map = hashmap<ast::node_id, method_origin>; @@ -556,12 +556,14 @@ fn ty_param_bounds(tcx: ty::ctxt, mode: mode, params: [ast::ty_param]) } fn ty_of_method(tcx: ty::ctxt, mode: mode, m: @ast::method) -> ty::method { {ident: m.ident, tps: ty_param_bounds(tcx, mode, m.tps), - fty: ty_of_fn_decl(tcx, mode, ast::proto_bare, m.decl)} + fty: ty_of_fn_decl(tcx, mode, ast::proto_bare, m.decl), + purity: m.decl.purity} } fn ty_of_ty_method(tcx: ty::ctxt, mode: mode, m: ast::ty_method) -> ty::method { {ident: m.ident, tps: ty_param_bounds(tcx, mode, m.tps), - fty: ty_of_fn_decl(tcx, mode, ast::proto_bare, m.decl)} + fty: ty_of_fn_decl(tcx, mode, ast::proto_bare, m.decl), + purity: m.decl.purity} } // A convenience function to use a crate_ctxt to resolve names for @@ -817,6 +819,12 @@ mod collect { alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) { some({mty: m, id, span}) { + if m.purity != if_m.purity { + cx.tcx.sess.span_err( + span, "method `" + m.ident + "`'s purity \ + not match the iface method's \ + purity"); + } let mt = compare_impl_method( cx.tcx, span, m, vec::len(tps), if_m, tys, selfty); @@ -1536,30 +1544,39 @@ fn require_impure(sess: 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::unsafe_fn { ret; } - ast::impure_fn { - alt ccx.tcx.def_map.find(callee.id) { - some(ast::def_fn(_, ast::unsafe_fn)) { - ccx.tcx.sess.span_err( - sp, - "safe function calls function marked unsafe"); + if caller_purity == ast::unsafe_fn { ret; } + let callee_purity = alt ccx.tcx.def_map.find(callee.id) { + some(ast::def_fn(_, p)) { p } + some(ast::def_variant(_, _)) { ast::pure_fn } + _ { + alt ccx.method_map.find(callee.id) { + some(method_static(did)) { + if did.crate == ast::local_crate { + alt ccx.tcx.items.get(did.node) { + ast_map::node_method(m, _, _) { m.decl.purity } + _ { fail; } + } + } else { + csearch::lookup_method_purity(ccx.tcx.sess.cstore, did) + } } - _ { + some(method_param(iid, n_m, _, _)) | some(method_iface(iid, n_m)) { + ty::iface_methods(ccx.tcx, iid)[n_m].purity } + none { ast::impure_fn } } - ret; } - ast::pure_fn { - alt ccx.tcx.def_map.find(callee.id) { - some(ast::def_fn(_, ast::pure_fn)) | - some(ast::def_variant(_, _)) { ret; } - _ { - ccx.tcx.sess.span_err - (sp, "pure function calls function not known to be pure"); - } - } + }; + alt (caller_purity, callee_purity) { + (ast::impure_fn, ast::unsafe_fn) { + ccx.tcx.sess.span_err(sp, "safe function calls function marked \ + unsafe"); } + (ast::pure_fn, ast::unsafe_fn) | (ast::pure_fn, ast::impure_fn) { + ccx.tcx.sess.span_err(sp, "pure function calls function not \ + known to be pure"); + } + _ {} } } @@ -1686,7 +1703,7 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr, ret some({method_ty: fty, n_tps: vec::len(*m.tps), substs: tps, - origin: method_iface(i), + origin: method_iface(did, i), self_sub: none}); } i += 1u; @@ -1875,12 +1892,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, // A generic function for doing all of the checking for call expressions fn check_call_full(fcx: @fn_ctxt, sp: span, f: @ast::expr, args: [@ast::expr], id: ast::node_id) -> bool { + let bot = check_call(fcx, sp, f, args); /* here we're kind of hosed, as f can be any expr need to restrict it to being an explicit expr_path if we're inside a pure function, and need an environment mapping from function name onto purity-designation */ require_pure_call(fcx.ccx, fcx.purity, f, sp); - let bot = check_call(fcx, sp, f, args); // Pull the return type out of the type of the function. let fty = ty::expr_ty(fcx.ccx.tcx, f); diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 3ce4d38013e..60a71ebe804 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -274,21 +274,22 @@ fn parse_ty_fn(p: parser) -> ast::fn_decl { let constrs: [@ast::constr] = []; let (ret_style, ret_ty) = parse_ret_ty(p); ret {inputs: inputs.node, output: ret_ty, - purity: ast::impure_fn, cf: ret_style, - constraints: constrs}; + purity: ast::impure_fn, cf: ret_style, + constraints: constrs}; } fn parse_ty_methods(p: parser) -> [ast::ty_method] { parse_seq(token::LBRACE, token::RBRACE, seq_sep_none(), {|p| let attrs = parse_outer_attributes(p); let flo = p.span.lo; - expect_word(p, "fn"); + let pur = parse_fn_purity(p); let ident = parse_method_name(p); let tps = parse_ty_params(p); let d = parse_ty_fn(p), fhi = p.last_span.hi; expect(p, token::SEMI); - {ident: ident, attrs: attrs, decl: d, tps: tps, - span: ast_util::mk_sp(flo, fhi)}}, p).node + {ident: ident, attrs: attrs, decl: {purity: pur with d}, tps: tps, + span: ast_util::mk_sp(flo, fhi)} + }, p).node } fn parse_mt(p: parser) -> ast::mt { @@ -1889,11 +1890,10 @@ fn parse_method_name(p: parser) -> ast::ident { fn parse_method(p: parser) -> @ast::method { let attrs = parse_outer_attributes(p); - let lo = p.span.lo; - expect_word(p, "fn"); + let lo = p.span.lo, pur = parse_fn_purity(p); let ident = parse_method_name(p); let tps = parse_ty_params(p); - let decl = parse_fn_decl(p, ast::impure_fn); + let decl = parse_fn_decl(p, pur); let (inner_attrs, body) = parse_inner_attrs_and_block(p, true); let attrs = attrs + inner_attrs; @{ident: ident, attrs: attrs, tps: tps, decl: decl, body: body, @@ -2109,17 +2109,16 @@ fn parse_item_native_fn(p: parser, attrs: [ast::attribute], span: ast_util::mk_sp(lo, hi)}; } +fn parse_fn_purity(p: parser) -> ast::purity { + if eat_word(p, "fn") { ast::impure_fn } + else if eat_word(p, "pure") { expect_word(p, "fn"); ast::pure_fn } + else if eat_word(p, "unsafe") { expect_word(p, "fn"); ast::unsafe_fn } + else { unexpected(p, p.token); } +} + fn parse_native_item(p: parser, attrs: [ast::attribute]) -> @ast::native_item { - if eat_word(p, "fn") { - ret parse_item_native_fn(p, attrs, ast::impure_fn); - } else if eat_word(p, "pure") { - expect_word(p, "fn"); - ret parse_item_native_fn(p, attrs, ast::pure_fn); - } else if eat_word(p, "unsafe") { - expect_word(p, "fn"); - ret parse_item_native_fn(p, attrs, ast::unsafe_fn); - } else { unexpected(p, p.token); } + parse_item_native_fn(p, attrs, parse_fn_purity(p)) } fn parse_native_mod_items(p: parser, first_item_attrs: [ast::attribute]) -> |
