|
| 1 | +import front::ast; |
| 2 | +import front::ast::ident; |
| 3 | +import front::ast::def_id; |
| 4 | +import std::vec; |
| 5 | +import std::str; |
| 6 | +import std::option; |
| 7 | +import std::option::some; |
| 8 | +import std::option::none; |
| 9 | + |
| 10 | +tag deref_t { |
| 11 | + field(ident); |
| 12 | + index; |
| 13 | + unbox; |
| 14 | +} |
| 15 | +type deref = rec(bool mut, deref_t t); |
| 16 | + |
| 17 | +type ctx = @rec(@ty::ctxt tcx, |
| 18 | + resolve::def_map dm, |
| 19 | + // The current blacklisted (non-assignable) locals |
| 20 | + mutable vec[vec[def_id]] bl, |
| 21 | + // A stack of blacklists for outer function scopes |
| 22 | + mutable vec[vec[vec[def_id]]] blstack); |
| 23 | + |
| 24 | +fn check_crate(@ty::ctxt tcx, resolve::def_map dm, &@ast::crate crate) { |
| 25 | + auto cx = @rec(tcx = tcx, |
| 26 | + dm = dm, |
| 27 | + mutable bl = vec::empty[vec[def_id]](), |
| 28 | + mutable blstack = vec::empty[vec[vec[def_id]]]()); |
| 29 | + auto v = rec(visit_item_pre = bind enter_item(cx, _), |
| 30 | + visit_item_post = bind leave_item(cx, _), |
| 31 | + visit_method_pre = bind enter_method(cx, _), |
| 32 | + visit_method_post = bind leave_method(cx, _), |
| 33 | + visit_expr_pre = bind check_expr(cx, _), |
| 34 | + visit_expr_post = bind leave_expr(cx, _) |
| 35 | + with walk::default_visitor()); |
| 36 | + walk::walk_crate(v, *crate); |
| 37 | +} |
| 38 | + |
| 39 | +fn enter_item(ctx cx, &@ast::item it) { |
| 40 | + alt (it.node) { |
| 41 | + case (ast::item_fn(_, _, _, _, _)) { |
| 42 | + vec::push(cx.blstack, cx.bl); |
| 43 | + cx.bl = []; |
| 44 | + } |
| 45 | + case (_) {} |
| 46 | + } |
| 47 | +} |
| 48 | +fn leave_item(ctx cx, &@ast::item it) { |
| 49 | + alt (it.node) { |
| 50 | + case (ast::item_fn(_, _, _, _, _)) { |
| 51 | + cx.bl = vec::pop(cx.blstack); |
| 52 | + } |
| 53 | + case (_) {} |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +fn enter_method(ctx cx, &@ast::method mt) { |
| 58 | + vec::push(cx.blstack, cx.bl); |
| 59 | + cx.bl = []; |
| 60 | +} |
| 61 | +fn leave_method(ctx cx, &@ast::method mt) { |
| 62 | + cx.bl = vec::pop(cx.blstack); |
| 63 | +} |
| 64 | + |
| 65 | +fn check_expr(ctx cx, &@ast::expr ex) { |
| 66 | + alt (ex.node) { |
| 67 | + case (ast::expr_call(?f, ?args, _)) { |
| 68 | + auto fty = ty::expr_ty(*cx.tcx, f); |
| 69 | + auto argtys = alt (ty::struct(*cx.tcx, fty)) { |
| 70 | + case (ty::ty_fn(_, ?args, _, _)) { args } |
| 71 | + case (ty::ty_native_fn(_, ?args, _)) { args } |
| 72 | + }; |
| 73 | + auto i = 0u; |
| 74 | + let vec[def_id] listed = []; |
| 75 | + for (ty::arg argty in argtys) { |
| 76 | + // FIXME Treat mo_either specially here? |
| 77 | + if (argty.mode != ty::mo_val) { |
| 78 | + alt (check_rooted(cx, args.(i), false)) { |
| 79 | + case (some(?did)) { |
| 80 | + vec::push(listed, did); |
| 81 | + } |
| 82 | + case (_) {} |
| 83 | + } |
| 84 | + } |
| 85 | + i += 1u; |
| 86 | + } |
| 87 | + // FIXME when mutable aliases can be distinguished, go over the |
| 88 | + // args again and ensure that we're not passing a blacklisted |
| 89 | + // variable by mutable alias (using 'listed' and the context |
| 90 | + // blacklist). |
| 91 | + } |
| 92 | + case (ast::expr_put(?val, _)) { |
| 93 | + alt (val) { |
| 94 | + case (some(?ex)) { check_rooted(cx, ex, false); } |
| 95 | + case (_) {} |
| 96 | + } |
| 97 | + } |
| 98 | + case (ast::expr_alt(?input, _, _)) { |
| 99 | + vec::push(cx.bl, alt (check_rooted(cx, input, true)) { |
| 100 | + case (some(?did)) { [did] } |
| 101 | + case (_) { vec::empty[def_id]() } |
| 102 | + }); |
| 103 | + } |
| 104 | + |
| 105 | + case (ast::expr_move(?dest, _, _)) { check_assign(cx, dest); } |
| 106 | + case (ast::expr_assign(?dest, _, _)) { check_assign(cx, dest); } |
| 107 | + case (ast::expr_assign_op(_, ?dest, _, _)) { check_assign(cx, dest); } |
| 108 | + case (_) {} |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +fn leave_expr(ctx cx, &@ast::expr ex) { |
| 113 | + alt (ex.node) { |
| 114 | + case (ast::expr_alt(_, _, _)) { vec::pop(cx.bl); } |
| 115 | + case (_) {} |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +fn check_assign(&ctx cx, &@ast::expr ex) { |
| 120 | + alt (ex.node) { |
| 121 | + case (ast::expr_path(?pt, ?ann)) { |
| 122 | + auto did = ast::def_id_of_def(cx.dm.get(ann.id)); |
| 123 | + for (vec[def_id] layer in cx.bl) { |
| 124 | + for (def_id black in layer) { |
| 125 | + if (did == black) { |
| 126 | + cx.tcx.sess.span_err |
| 127 | + (ex.span, str::connect(pt.node.idents, "::") + |
| 128 | + " is being aliased and may not be assigned to"); |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + case (_) {} |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +fn check_rooted(&ctx cx, &@ast::expr ex, bool autoderef) |
| 138 | + -> option::t[def_id] { |
| 139 | + auto root = expr_root(cx, ex, autoderef); |
| 140 | + if (has_unsafe_box(root.ds)) { |
| 141 | + cx.tcx.sess.span_err |
| 142 | + (ex.span, "can not create alias to improperly anchored value"); |
| 143 | + } |
| 144 | + alt (root.ex.node) { |
| 145 | + case (ast::expr_path(_, ?ann)) { |
| 146 | + ret some(ast::def_id_of_def(cx.dm.get(ann.id))); |
| 147 | + } |
| 148 | + case (_) { |
| 149 | + ret none[def_id]; |
| 150 | + } |
| 151 | + } |
| 152 | +} |
| 153 | + |
| 154 | +fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) |
| 155 | + -> rec(@ast::expr ex, vec[deref] ds) { |
| 156 | + let vec[deref] ds = []; |
| 157 | + if (autoderef) { |
| 158 | + auto auto_unbox = maybe_auto_unbox(cx, ex); |
| 159 | + if (auto_unbox.done) { |
| 160 | + vec::push(ds, rec(mut=auto_unbox.mut, t=unbox)); |
| 161 | + } |
| 162 | + } |
| 163 | + while (true) { |
| 164 | + alt ({ex.node}) { |
| 165 | + case (ast::expr_field(?base, ?ident, _)) { |
| 166 | + auto auto_unbox = maybe_auto_unbox(cx, base); |
| 167 | + alt (auto_unbox.t) { |
| 168 | + case (ty::ty_tup(?fields)) { |
| 169 | + auto fnm = ty::field_num(cx.tcx.sess, ex.span, ident); |
| 170 | + auto mt = fields.(fnm).mut != ast::imm; |
| 171 | + vec::push(ds, rec(mut=mt, t=field(ident))); |
| 172 | + } |
| 173 | + case (ty::ty_rec(?fields)) { |
| 174 | + for (ty::field fld in fields) { |
| 175 | + if (str::eq(ident, fld.ident)) { |
| 176 | + auto mt = fld.mt.mut != ast::imm; |
| 177 | + vec::push(ds, rec(mut=mt, t=field(ident))); |
| 178 | + break; |
| 179 | + } |
| 180 | + } |
| 181 | + } |
| 182 | + case (ty::ty_obj(_)) { |
| 183 | + vec::push(ds, rec(mut=false, t=field(ident))); |
| 184 | + } |
| 185 | + } |
| 186 | + if (auto_unbox.done) { |
| 187 | + vec::push(ds, rec(mut=auto_unbox.mut, t=unbox)); |
| 188 | + } |
| 189 | + ex = base; |
| 190 | + } |
| 191 | + case (ast::expr_index(?base, _, _)) { |
| 192 | + auto auto_unbox = maybe_auto_unbox(cx, base); |
| 193 | + alt (auto_unbox.t) { |
| 194 | + case (ty::ty_vec(?mt)) { |
| 195 | + vec::push(ds, rec(mut=mt.mut != ast::imm, t=index)); |
| 196 | + } |
| 197 | + } |
| 198 | + if (auto_unbox.done) { |
| 199 | + vec::push(ds, rec(mut=auto_unbox.mut, t=unbox)); |
| 200 | + } |
| 201 | + ex = base; |
| 202 | + } |
| 203 | + case (ast::expr_unary(?op, ?base, _)) { |
| 204 | + if (op == ast::deref) { |
| 205 | + alt (ty::struct(*cx.tcx, ty::expr_ty(*cx.tcx, base))) { |
| 206 | + case (ty::ty_box(?mt)) { |
| 207 | + vec::push(ds, rec(mut=mt.mut!=ast::imm, t=unbox)); |
| 208 | + } |
| 209 | + } |
| 210 | + ex = base; |
| 211 | + } else { |
| 212 | + break; |
| 213 | + } |
| 214 | + } |
| 215 | + case (_) { break; } |
| 216 | + } |
| 217 | + } |
| 218 | + vec::reverse(ds); |
| 219 | + ret rec(ex = ex, ds = ds); |
| 220 | +} |
| 221 | + |
| 222 | +fn maybe_auto_unbox(&ctx cx, &@ast::expr ex) |
| 223 | + -> rec(ty::sty t, bool done, bool mut) { |
| 224 | + auto tp = ty::struct(*cx.tcx, ty::expr_ty(*cx.tcx, ex)); |
| 225 | + alt (tp) { |
| 226 | + case (ty::ty_box(?mt)) { |
| 227 | + ret rec(t=ty::struct(*cx.tcx, mt.ty), |
| 228 | + done=true, mut=mt.mut != ast::imm); |
| 229 | + } |
| 230 | + case (_) { ret rec(t=tp, done=false, mut=false); } |
| 231 | + } |
| 232 | +} |
| 233 | + |
| 234 | +fn has_unsafe_box(&vec[deref] ds) -> bool { |
| 235 | + auto saw_mut = false; |
| 236 | + for (deref d in ds) { |
| 237 | + if (d.mut) { saw_mut = true; } |
| 238 | + if (d.t == unbox) { |
| 239 | + // Directly aliasing the content of a mutable box is never okay, |
| 240 | + // and any box living under mutable connection may be severed from |
| 241 | + // its root and freed. |
| 242 | + if (saw_mut) { ret true; } |
| 243 | + } |
| 244 | + } |
| 245 | + ret false; |
| 246 | +} |
| 247 | + |
| 248 | +// Local Variables: |
| 249 | +// mode: rust |
| 250 | +// fill-column: 78; |
| 251 | +// indent-tabs-mode: nil |
| 252 | +// c-basic-offset: 4 |
| 253 | +// buffer-file-coding-system: utf-8-unix |
| 254 | +// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; |
| 255 | +// End: |
0 commit comments