2011-11-15 11:15:35 -06:00
|
|
|
import syntax::{visit, ast_util};
|
|
|
|
import syntax::ast::*;
|
|
|
|
import syntax::codemap::span;
|
2011-12-28 11:11:33 -06:00
|
|
|
import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable};
|
2012-01-12 10:59:49 -06:00
|
|
|
import driver::session::session;
|
2012-03-07 18:48:57 -06:00
|
|
|
import std::map::hashmap;
|
2011-11-15 11:15:35 -06:00
|
|
|
|
2011-11-18 10:09:14 -06:00
|
|
|
// Kind analysis pass. There are three kinds:
|
|
|
|
//
|
|
|
|
// sendable: scalar types, and unique types containing only sendable types
|
2012-01-13 03:58:31 -06:00
|
|
|
// copyable: boxes, closures, and uniques containing copyable types
|
2011-11-18 10:09:14 -06:00
|
|
|
// noncopyable: resources, or unique types containing resources
|
|
|
|
//
|
|
|
|
// This pass ensures that type parameters are only instantiated with types
|
|
|
|
// whose kinds are equal or less general than the way the type parameter was
|
|
|
|
// annotated (with the `send` or `copy` keyword).
|
|
|
|
//
|
|
|
|
// It also verifies that noncopyable kinds are not copied. Sendability is not
|
|
|
|
// applied, since none of our language primitives send. Instead, the sending
|
|
|
|
// primitives in the stdlib are explicitly annotated to only take sendable
|
|
|
|
// types.
|
|
|
|
|
2011-11-15 11:15:35 -06:00
|
|
|
fn kind_to_str(k: kind) -> str {
|
|
|
|
alt k {
|
2012-01-19 00:37:22 -06:00
|
|
|
kind_sendable { "sendable" }
|
|
|
|
kind_copyable { "copyable" }
|
|
|
|
kind_noncopyable { "noncopyable" }
|
2011-11-15 11:15:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type rval_map = std::map::hashmap<node_id, ()>;
|
|
|
|
|
|
|
|
type ctx = {tcx: ty::ctxt,
|
|
|
|
rval_map: rval_map,
|
2011-12-14 07:38:25 -06:00
|
|
|
method_map: typeck::method_map,
|
2011-11-23 03:56:10 -06:00
|
|
|
last_uses: last_use::last_uses};
|
2011-11-15 11:15:35 -06:00
|
|
|
|
2011-12-14 07:38:25 -06:00
|
|
|
fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
|
|
|
|
last_uses: last_use::last_uses, crate: @crate)
|
|
|
|
-> rval_map {
|
2011-11-15 11:15:35 -06:00
|
|
|
let ctx = {tcx: tcx,
|
2012-03-14 14:07:23 -05:00
|
|
|
rval_map: std::map::int_hash(),
|
2011-12-14 07:38:25 -06:00
|
|
|
method_map: method_map,
|
2011-11-23 03:56:10 -06:00
|
|
|
last_uses: last_uses};
|
2011-11-15 11:15:35 -06:00
|
|
|
let visit = visit::mk_vt(@{
|
|
|
|
visit_expr: check_expr,
|
2011-12-20 21:39:33 -06:00
|
|
|
visit_stmt: check_stmt,
|
2012-01-02 08:23:11 -06:00
|
|
|
visit_block: check_block,
|
2012-03-19 04:45:29 -05:00
|
|
|
visit_fn: check_fn,
|
|
|
|
visit_ty: check_ty
|
2011-11-15 11:15:35 -06:00
|
|
|
with *visit::default_visitor()
|
|
|
|
});
|
|
|
|
visit::visit_crate(*crate, ctx, visit);
|
|
|
|
tcx.sess.abort_if_errors();
|
|
|
|
ret ctx.rval_map;
|
|
|
|
}
|
|
|
|
|
2011-12-20 21:39:33 -06:00
|
|
|
// Yields the appropriate function to check the kind of closed over
|
|
|
|
// variables. `id` is the node_id for some expression that creates the
|
|
|
|
// closure.
|
2012-01-09 08:49:56 -06:00
|
|
|
fn with_appropriate_checker(cx: ctx, id: node_id,
|
2012-01-23 16:59:00 -06:00
|
|
|
b: fn(fn@(ctx, ty::t, sp: span))) {
|
2012-01-30 10:28:30 -06:00
|
|
|
let fty = ty::node_id_to_type(cx.tcx, id);
|
2012-02-03 08:15:28 -06:00
|
|
|
alt ty::ty_fn_proto(fty) {
|
2012-01-19 00:37:22 -06:00
|
|
|
proto_uniq { b(check_send); }
|
|
|
|
proto_box { b(check_copy); }
|
|
|
|
proto_bare { b(check_none); }
|
|
|
|
proto_any | proto_block { /* no check needed */ }
|
2011-12-20 21:39:33 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the free variables used in a shared/sendable closure conform
|
|
|
|
// to the copy/move kind bounds. Then recursively check the function body.
|
2011-12-29 22:07:55 -06:00
|
|
|
fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
|
2012-02-29 11:53:30 -06:00
|
|
|
fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {
|
2011-12-09 18:56:48 -06:00
|
|
|
|
2011-12-20 21:39:33 -06:00
|
|
|
// n.b.: This could be the body of either a fn decl or a fn expr. In the
|
|
|
|
// former case, the prototype will be proto_bare and no check occurs. In
|
|
|
|
// the latter case, we do not check the variables that in the capture
|
|
|
|
// clause (as we don't have access to that here) but just those that
|
|
|
|
// appear free. The capture clauses are checked below, in check_expr().
|
|
|
|
//
|
|
|
|
// We could do this check also in check_expr(), but it seems more
|
|
|
|
// "future-proof" to do it this way, as check_fn_body() is supposed to be
|
|
|
|
// the common flow point for all functions that appear in the AST.
|
|
|
|
|
2012-02-29 11:53:30 -06:00
|
|
|
with_appropriate_checker(cx, fn_id) { |checker|
|
|
|
|
for @{def, span} in *freevars::get_freevars(cx.tcx, fn_id) {
|
2011-12-16 18:07:54 -06:00
|
|
|
let id = ast_util::def_id_of_def(def).node;
|
2012-02-29 11:53:30 -06:00
|
|
|
if checker == check_copy {
|
|
|
|
let last_uses = alt check cx.last_uses.find(fn_id) {
|
|
|
|
some(last_use::closes_over(vars)) { vars }
|
|
|
|
none { [] }
|
|
|
|
};
|
2012-03-18 19:14:43 -05:00
|
|
|
if option::is_some(
|
|
|
|
vec::position_elem(last_uses, id)) { cont; }
|
2012-02-29 12:07:23 -06:00
|
|
|
}
|
2011-12-09 18:56:48 -06:00
|
|
|
let ty = ty::node_id_to_type(cx.tcx, id);
|
2012-01-09 08:49:56 -06:00
|
|
|
checker(cx, ty, span);
|
2011-12-09 18:56:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-29 11:53:30 -06:00
|
|
|
visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
|
2011-12-20 21:39:33 -06:00
|
|
|
}
|
|
|
|
|
2011-12-20 23:56:21 -06:00
|
|
|
fn check_fn_cap_clause(cx: ctx,
|
|
|
|
id: node_id,
|
|
|
|
cap_clause: capture_clause) {
|
|
|
|
// Check that the variables named in the clause which are not free vars
|
2012-01-09 08:49:56 -06:00
|
|
|
// (if any) are also legal. freevars are checked above in check_fn().
|
2011-12-20 23:56:21 -06:00
|
|
|
// This is kind of a degenerate case, as captured variables will generally
|
|
|
|
// appear free in the body.
|
|
|
|
let freevars = freevars::get_freevars(cx.tcx, id);
|
|
|
|
let freevar_ids = vec::map(*freevars, { |freevar|
|
|
|
|
ast_util::def_id_of_def(freevar.def).node
|
|
|
|
});
|
|
|
|
//log("freevar_ids", freevar_ids);
|
2012-01-09 08:49:56 -06:00
|
|
|
with_appropriate_checker(cx, id) { |checker|
|
2012-01-09 18:12:37 -06:00
|
|
|
let check_var = fn@(&&cap_item: @capture_item) {
|
2011-12-20 23:56:21 -06:00
|
|
|
let cap_def = cx.tcx.def_map.get(cap_item.id);
|
|
|
|
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
|
2012-02-11 19:54:49 -06:00
|
|
|
if !vec::contains(freevar_ids, cap_def_id) {
|
2011-12-20 23:56:21 -06:00
|
|
|
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
|
2012-01-09 08:49:56 -06:00
|
|
|
checker(cx, ty, cap_item.span);
|
2011-12-20 23:56:21 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
vec::iter(cap_clause.copies, check_var);
|
|
|
|
vec::iter(cap_clause.moves, check_var);
|
|
|
|
}
|
2011-12-20 21:39:33 -06:00
|
|
|
}
|
|
|
|
|
2012-01-02 08:23:11 -06:00
|
|
|
fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
|
|
|
|
alt b.node.expr {
|
|
|
|
some(ex) { maybe_copy(cx, ex); }
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
visit::visit_block(b, cx, v);
|
|
|
|
}
|
2011-12-20 21:39:33 -06:00
|
|
|
|
2012-01-02 08:23:11 -06:00
|
|
|
fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
2011-11-15 11:15:35 -06:00
|
|
|
alt e.node {
|
|
|
|
expr_assign(_, ex) | expr_assign_op(_, _, ex) |
|
2012-01-02 08:23:11 -06:00
|
|
|
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
|
2011-11-23 03:56:10 -06:00
|
|
|
expr_ret(some(ex)) { maybe_copy(cx, ex); }
|
2011-11-15 11:15:35 -06:00
|
|
|
expr_copy(expr) { check_copy_ex(cx, expr, false); }
|
|
|
|
// Vector add copies.
|
2012-01-19 00:37:22 -06:00
|
|
|
expr_binary(add, ls, rs) { maybe_copy(cx, ls); maybe_copy(cx, rs); }
|
2011-11-22 06:27:40 -06:00
|
|
|
expr_rec(fields, def) {
|
2011-11-15 11:15:35 -06:00
|
|
|
for field in fields { maybe_copy(cx, field.node.expr); }
|
2011-11-22 06:27:40 -06:00
|
|
|
alt def {
|
|
|
|
some(ex) {
|
|
|
|
// All noncopyable fields must be overridden
|
|
|
|
let t = ty::expr_ty(cx.tcx, ex);
|
2012-02-03 08:15:28 -06:00
|
|
|
let ty_fields = alt ty::get(t).struct {
|
|
|
|
ty::ty_rec(f) { f }
|
2012-03-05 18:27:27 -06:00
|
|
|
_ { cx.tcx.sess.span_bug(ex.span, "bad expr type in record"); }
|
2012-02-03 08:15:28 -06:00
|
|
|
};
|
2011-11-22 06:27:40 -06:00
|
|
|
for tf in ty_fields {
|
2011-12-16 08:27:50 -06:00
|
|
|
if !vec::any(fields, {|f| f.node.ident == tf.ident}) &&
|
2011-12-28 11:11:33 -06:00
|
|
|
!ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) {
|
2011-11-22 06:27:40 -06:00
|
|
|
cx.tcx.sess.span_err(ex.span,
|
|
|
|
"copying a noncopyable value");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
2011-11-15 11:15:35 -06:00
|
|
|
}
|
|
|
|
expr_tup(exprs) | expr_vec(exprs, _) {
|
|
|
|
for expr in exprs { maybe_copy(cx, expr); }
|
|
|
|
}
|
|
|
|
expr_bind(_, args) {
|
|
|
|
for a in args { alt a { some(ex) { maybe_copy(cx, ex); } _ {} } }
|
|
|
|
}
|
2011-11-18 03:20:51 -06:00
|
|
|
expr_call(f, args, _) {
|
2012-03-15 08:47:03 -05:00
|
|
|
let mut i = 0u;
|
2012-02-03 08:15:28 -06:00
|
|
|
for arg_t in ty::ty_fn_args(ty::expr_ty(cx.tcx, f)) {
|
2012-02-02 18:50:17 -06:00
|
|
|
alt ty::arg_mode(cx.tcx, arg_t) {
|
|
|
|
by_copy { maybe_copy(cx, args[i]); }
|
2012-02-15 13:25:39 -06:00
|
|
|
by_ref | by_val | by_mutbl_ref | by_move { }
|
2012-02-02 18:50:17 -06:00
|
|
|
}
|
2011-11-18 03:20:51 -06:00
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
}
|
2012-03-16 10:25:36 -05:00
|
|
|
expr_path(_) | expr_field(_, _, _) {
|
2012-04-02 17:34:49 -05:00
|
|
|
option::with_option_do(cx.tcx.node_type_substs.find(e.id)) {|ts|
|
2012-03-16 10:25:36 -05:00
|
|
|
let bounds = alt check e.node {
|
|
|
|
expr_path(_) {
|
|
|
|
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
|
|
|
|
ty::lookup_item_type(cx.tcx, did).bounds
|
|
|
|
}
|
2012-04-02 12:48:32 -05:00
|
|
|
expr_field(base, _, _) {
|
2012-03-16 10:25:36 -05:00
|
|
|
alt cx.method_map.get(e.id) {
|
|
|
|
typeck::method_static(did) {
|
2012-04-02 12:48:32 -05:00
|
|
|
/*
|
|
|
|
If this is a class method, we want to use the
|
|
|
|
class bounds plus the method bounds -- otherwise the
|
|
|
|
indices come out wrong. So we check base's type...
|
|
|
|
*/
|
|
|
|
let mut bounds = ty::lookup_item_type(cx.tcx, did).bounds;
|
|
|
|
alt ty::get(ty::node_id_to_type(cx.tcx, base.id)).struct {
|
|
|
|
ty::ty_class(parent_id, ts) {
|
|
|
|
/* ...and if it has a class type, prepend the
|
|
|
|
class bounds onto the method bounds */
|
|
|
|
bounds =
|
|
|
|
@(*ty::lookup_item_type(cx.tcx, parent_id).bounds
|
|
|
|
+ *bounds);
|
|
|
|
}
|
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
bounds
|
2012-03-16 10:25:36 -05:00
|
|
|
}
|
|
|
|
typeck::method_param(ifce_id, n_mth, _, _) |
|
|
|
|
typeck::method_iface(ifce_id, n_mth) {
|
|
|
|
let ifce_bounds =
|
|
|
|
ty::lookup_item_type(cx.tcx, ifce_id).bounds;
|
|
|
|
let mth = ty::iface_methods(cx.tcx, ifce_id)[n_mth];
|
|
|
|
@(*ifce_bounds + *mth.tps)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
vec::iter2(ts, *bounds) {|ty, bound|
|
2012-03-19 04:45:29 -05:00
|
|
|
check_bounds(cx, e.span, ty, bound)
|
2011-11-18 03:20:51 -06:00
|
|
|
}
|
|
|
|
}
|
2011-11-15 11:15:35 -06:00
|
|
|
}
|
2011-12-29 22:07:55 -06:00
|
|
|
expr_fn(_, _, _, cap_clause) {
|
2011-12-22 10:49:54 -06:00
|
|
|
check_fn_cap_clause(cx, e.id, *cap_clause);
|
|
|
|
}
|
2011-11-15 11:15:35 -06:00
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
visit::visit_expr(e, cx, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
|
|
|
|
alt stmt.node {
|
|
|
|
stmt_decl(@{node: decl_local(locals), _}, _) {
|
2012-02-10 07:33:36 -06:00
|
|
|
for local in locals {
|
2011-11-15 11:15:35 -06:00
|
|
|
alt local.node.init {
|
2012-01-19 00:37:22 -06:00
|
|
|
some({op: init_assign, expr}) { maybe_copy(cx, expr); }
|
2011-11-15 11:15:35 -06:00
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
visit::visit_stmt(stmt, cx, v);
|
|
|
|
}
|
|
|
|
|
2012-03-19 04:45:29 -05:00
|
|
|
fn check_ty(aty: @ty, cx: ctx, v: visit::vt<ctx>) {
|
|
|
|
alt aty.node {
|
|
|
|
ty_path(_, id) {
|
2012-04-02 17:34:49 -05:00
|
|
|
option::with_option_do(cx.tcx.node_type_substs.find(id)) {|ts|
|
2012-03-19 04:45:29 -05:00
|
|
|
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(id));
|
|
|
|
let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
|
|
|
|
vec::iter2(ts, *bounds) {|ty, bound|
|
|
|
|
check_bounds(cx, aty.span, ty, bound)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
visit::visit_ty(aty, cx, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_bounds(cx: ctx, sp: span, ty: ty::t, bounds: ty::param_bounds) {
|
|
|
|
let kind = ty::type_kind(cx.tcx, ty);
|
|
|
|
let p_kind = ty::param_bounds_to_kind(bounds);
|
|
|
|
if !ty::kind_lteq(p_kind, kind) {
|
|
|
|
cx.tcx.sess.span_err(sp, "instantiating a " +
|
|
|
|
kind_to_str(p_kind) +
|
|
|
|
" type parameter with a "
|
|
|
|
+ kind_to_str(kind) + " type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-15 11:15:35 -06:00
|
|
|
fn maybe_copy(cx: ctx, ex: @expr) {
|
|
|
|
check_copy_ex(cx, ex, true);
|
|
|
|
}
|
|
|
|
|
2012-01-31 05:34:22 -06:00
|
|
|
fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
|
|
|
|
alt ex.node {
|
|
|
|
expr_path(_) {
|
|
|
|
alt cx.tcx.def_map.get(ex.id) {
|
|
|
|
def_variant(edid, vdid) {
|
|
|
|
vec::len(ty::enum_variant_with_id(cx.tcx, edid, vdid).args) == 0u
|
|
|
|
}
|
|
|
|
_ { false }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { false }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-15 11:15:35 -06:00
|
|
|
fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
|
2012-01-13 03:58:31 -06:00
|
|
|
if ty::expr_is_lval(cx.method_map, ex) &&
|
2012-01-31 05:34:22 -06:00
|
|
|
!cx.last_uses.contains_key(ex.id) &&
|
|
|
|
!is_nullary_variant(cx, ex) {
|
2011-11-15 11:15:35 -06:00
|
|
|
let ty = ty::expr_ty(cx.tcx, ex);
|
|
|
|
check_copy(cx, ty, ex.span);
|
|
|
|
// FIXME turn this on again once vector types are no longer unique.
|
|
|
|
// Right now, it is too annoying to be useful.
|
2012-02-03 08:15:28 -06:00
|
|
|
/* if warn && ty::type_is_unique(ty) {
|
2011-11-15 11:15:35 -06:00
|
|
|
cx.tcx.sess.span_warn(ex.span, "copying a unique value");
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_copy(cx: ctx, ty: ty::t, sp: span) {
|
2011-12-28 11:11:33 -06:00
|
|
|
if !ty::kind_can_be_copied(ty::type_kind(cx.tcx, ty)) {
|
2011-11-15 11:15:35 -06:00
|
|
|
cx.tcx.sess.span_err(sp, "copying a noncopyable value");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-09 18:56:48 -06:00
|
|
|
fn check_send(cx: ctx, ty: ty::t, sp: span) {
|
2011-12-28 11:11:33 -06:00
|
|
|
if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) {
|
2011-12-09 18:56:48 -06:00
|
|
|
cx.tcx.sess.span_err(sp, "not a sendable value");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-09 08:49:56 -06:00
|
|
|
fn check_none(cx: ctx, _ty: ty::t, sp: span) {
|
|
|
|
cx.tcx.sess.span_err(sp, "attempted dynamic environment capture");
|
|
|
|
}
|
|
|
|
|
2011-07-27 19:49:00 -05:00
|
|
|
//
|
|
|
|
// Local Variables:
|
|
|
|
// mode: rust
|
|
|
|
// fill-column: 78;
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
// c-basic-offset: 4
|
|
|
|
// buffer-file-coding-system: utf-8-unix
|
|
|
|
// End:
|
|
|
|
//
|