about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2012-02-13 18:56:09 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2012-02-13 21:08:05 +0100
commit6c9d95a9a0f82895559dc1dae6300ee14c304d7e (patch)
tree0459bf59264521a7168ccf4266ca0859f45d8a99
parent9caca02dac6bb9318aaa2f9ac52868b8ccead3e0 (diff)
downloadrust-6c9d95a9a0f82895559dc1dae6300ee14c304d7e.tar.gz
rust-6c9d95a9a0f82895559dc1dae6300ee14c304d7e.zip
Track purity/unsafety of iface and impl methods
Closes #1807
-rw-r--r--src/comp/metadata/csearch.rs10
-rw-r--r--src/comp/metadata/decoder.rs7
-rw-r--r--src/comp/metadata/encoder.rs17
-rw-r--r--src/comp/middle/trans/impl.rs14
-rw-r--r--src/comp/middle/ty.rs5
-rw-r--r--src/comp/middle/typeck.rs65
-rw-r--r--src/comp/syntax/parse/parser.rs33
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]) ->