]> git.lizzy.rs Git - rust.git/blob - src/comp/middle/kind.rs
Remove proto_sugar and 'lambda' as keyword, commit to fn@.
[rust.git] / src / comp / middle / kind.rs
1 import option::{some, none};
2 import syntax::{visit, ast_util};
3 import syntax::ast::*;
4 import syntax::codemap::span;
5 import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable};
6
7 // Kind analysis pass. There are three kinds:
8 //
9 //  sendable: scalar types, and unique types containing only sendable types
10 //  copyable: boxes, objects, closures, and uniques containing copyable types
11 //  noncopyable: resources, or unique types containing resources
12 //
13 // This pass ensures that type parameters are only instantiated with types
14 // whose kinds are equal or less general than the way the type parameter was
15 // annotated (with the `send` or `copy` keyword).
16 //
17 // It also verifies that noncopyable kinds are not copied. Sendability is not
18 // applied, since none of our language primitives send. Instead, the sending
19 // primitives in the stdlib are explicitly annotated to only take sendable
20 // types.
21
22 fn kind_to_str(k: kind) -> str {
23     alt k {
24       kind_sendable. { "sendable" }
25       kind_copyable. { "copyable" }
26       kind_noncopyable. { "noncopyable" }
27     }
28 }
29
30 type rval_map = std::map::hashmap<node_id, ()>;
31
32 type ctx = {tcx: ty::ctxt,
33             rval_map: rval_map,
34             method_map: typeck::method_map,
35             last_uses: last_use::last_uses};
36
37 fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
38                last_uses: last_use::last_uses, crate: @crate)
39     -> rval_map {
40     let ctx = {tcx: tcx,
41                rval_map: std::map::new_int_hash(),
42                method_map: method_map,
43                last_uses: last_uses};
44     let visit = visit::mk_vt(@{
45         visit_expr: check_expr,
46         visit_stmt: check_stmt,
47         visit_block: check_block,
48         visit_fn: check_fn
49         with *visit::default_visitor()
50     });
51     visit::visit_crate(*crate, ctx, visit);
52     tcx.sess.abort_if_errors();
53     ret ctx.rval_map;
54 }
55
56 // Yields the appropriate function to check the kind of closed over
57 // variables. `id` is the node_id for some expression that creates the
58 // closure.
59 fn with_appropriate_checker(cx: ctx, id: node_id,
60                             b: block(fn(ctx, ty::t, sp: span))) {
61     let fty = ty::node_id_to_monotype(cx.tcx, id);
62     alt ty::ty_fn_proto(cx.tcx, fty) {
63       proto_send. { b(check_send); }
64       proto_shared. { b(check_copy); }
65       proto_block. { /* no check needed */ }
66       proto_bare. { b(check_none); }
67     }
68 }
69
70 // Check that the free variables used in a shared/sendable closure conform
71 // to the copy/move kind bounds. Then recursively check the function body.
72 fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
73             id: node_id, cx: ctx, v: visit::vt<ctx>) {
74
75     // n.b.: This could be the body of either a fn decl or a fn expr.  In the
76     // former case, the prototype will be proto_bare and no check occurs.  In
77     // the latter case, we do not check the variables that in the capture
78     // clause (as we don't have access to that here) but just those that
79     // appear free.  The capture clauses are checked below, in check_expr().
80     //
81     // We could do this check also in check_expr(), but it seems more
82     // "future-proof" to do it this way, as check_fn_body() is supposed to be
83     // the common flow point for all functions that appear in the AST.
84
85     with_appropriate_checker(cx, id) { |checker|
86         for @{def, span} in *freevars::get_freevars(cx.tcx, id) {
87             let id = ast_util::def_id_of_def(def).node;
88             let ty = ty::node_id_to_type(cx.tcx, id);
89             checker(cx, ty, span);
90         }
91     }
92
93     visit::visit_fn(fk, decl, body, sp, id, cx, v);
94 }
95
96 fn check_fn_cap_clause(cx: ctx,
97                        id: node_id,
98                        cap_clause: capture_clause) {
99     // Check that the variables named in the clause which are not free vars
100     // (if any) are also legal.  freevars are checked above in check_fn().
101     // This is kind of a degenerate case, as captured variables will generally
102     // appear free in the body.
103     let freevars = freevars::get_freevars(cx.tcx, id);
104     let freevar_ids = vec::map(*freevars, { |freevar|
105         ast_util::def_id_of_def(freevar.def).node
106     });
107     //log("freevar_ids", freevar_ids);
108     with_appropriate_checker(cx, id) { |checker|
109         let check_var = fn@(&&cap_item: @capture_item) {
110             let cap_def = cx.tcx.def_map.get(cap_item.id);
111             let cap_def_id = ast_util::def_id_of_def(cap_def).node;
112             if !vec::member(cap_def_id, freevar_ids) {
113                 let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
114                 checker(cx, ty, cap_item.span);
115             }
116         };
117         vec::iter(cap_clause.copies, check_var);
118         vec::iter(cap_clause.moves, check_var);
119     }
120 }
121
122 fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
123     alt b.node.expr {
124       some(ex) { maybe_copy(cx, ex); }
125       _ {}
126     }
127     visit::visit_block(b, cx, v);
128 }
129
130 fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
131     alt e.node {
132       expr_assign(_, ex) | expr_assign_op(_, _, ex) |
133       expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
134       expr_ret(some(ex)) { maybe_copy(cx, ex); }
135       expr_copy(expr) { check_copy_ex(cx, expr, false); }
136       // Vector add copies.
137       expr_binary(add., ls, rs) { maybe_copy(cx, ls); maybe_copy(cx, rs); }
138       expr_rec(fields, def) {
139         for field in fields { maybe_copy(cx, field.node.expr); }
140         alt def {
141           some(ex) {
142             // All noncopyable fields must be overridden
143             let t = ty::expr_ty(cx.tcx, ex);
144             let ty_fields = alt ty::struct(cx.tcx, t) { ty::ty_rec(f) { f } };
145             for tf in ty_fields {
146                 if !vec::any(fields, {|f| f.node.ident == tf.ident}) &&
147                     !ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) {
148                     cx.tcx.sess.span_err(ex.span,
149                                          "copying a noncopyable value");
150                 }
151             }
152           }
153           _ {}
154         }
155       }
156       expr_tup(exprs) | expr_vec(exprs, _) {
157         for expr in exprs { maybe_copy(cx, expr); }
158       }
159       expr_bind(_, args) {
160         for a in args { alt a { some(ex) { maybe_copy(cx, ex); } _ {} } }
161       }
162       expr_call(f, args, _) {
163         let i = 0u;
164         for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) {
165             alt arg_t.mode { by_copy. { maybe_copy(cx, args[i]); } _ {} }
166             i += 1u;
167         }
168       }
169       expr_path(_) {
170         let substs = ty::node_id_to_ty_param_substs_opt_and_ty(cx.tcx, e.id);
171         alt substs.substs {
172           some(ts) {
173             let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
174             let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
175             let i = 0u;
176             for ty in ts {
177                 let kind = ty::type_kind(cx.tcx, ty);
178                 let p_kind = ty::param_bounds_to_kind(bounds[i]);
179                 if !ty::kind_lteq(p_kind, kind) {
180                     cx.tcx.sess.span_err(e.span, "instantiating a " +
181                                          kind_to_str(p_kind) +
182                                          " type parameter with a "
183                                          + kind_to_str(kind) + " type");
184                 }
185                 i += 1u;
186             }
187           }
188           none. {}
189         }
190       }
191       expr_ternary(_, a, b) { maybe_copy(cx, a); maybe_copy(cx, b); }
192       expr_fn(_, _, _, cap_clause) {
193         check_fn_cap_clause(cx, e.id, *cap_clause);
194       }
195       _ { }
196     }
197     visit::visit_expr(e, cx, v);
198 }
199
200 fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
201     alt stmt.node {
202       stmt_decl(@{node: decl_local(locals), _}, _) {
203         for (_, local) in locals {
204             alt local.node.init {
205               some({op: init_assign., expr}) { maybe_copy(cx, expr); }
206               _ {}
207             }
208         }
209       }
210       _ {}
211     }
212     visit::visit_stmt(stmt, cx, v);
213 }
214
215 fn maybe_copy(cx: ctx, ex: @expr) {
216     check_copy_ex(cx, ex, true);
217 }
218
219 fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
220     if ty::expr_is_lval(cx.method_map, cx.tcx, ex) &&
221        !cx.last_uses.contains_key(ex.id) {
222         let ty = ty::expr_ty(cx.tcx, ex);
223         check_copy(cx, ty, ex.span);
224         // FIXME turn this on again once vector types are no longer unique.
225         // Right now, it is too annoying to be useful.
226         /* if warn && ty::type_is_unique(cx.tcx, ty) {
227             cx.tcx.sess.span_warn(ex.span, "copying a unique value");
228         }*/
229     }
230 }
231
232 fn check_copy(cx: ctx, ty: ty::t, sp: span) {
233     if !ty::kind_can_be_copied(ty::type_kind(cx.tcx, ty)) {
234         cx.tcx.sess.span_err(sp, "copying a noncopyable value");
235     }
236 }
237
238 fn check_send(cx: ctx, ty: ty::t, sp: span) {
239     if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) {
240         cx.tcx.sess.span_err(sp, "not a sendable value");
241     }
242 }
243
244 fn check_none(cx: ctx, _ty: ty::t, sp: span) {
245     cx.tcx.sess.span_err(sp, "attempted dynamic environment capture");
246 }
247
248 //
249 // Local Variables:
250 // mode: rust
251 // fill-column: 78;
252 // indent-tabs-mode: nil
253 // c-basic-offset: 4
254 // buffer-file-coding-system: utf-8-unix
255 // End:
256 //