Skip to content

Commit 0c97fcb

Browse files
committed
Properly check kinds in instantiation of generics
Issue #1177
1 parent 7bef89f commit 0c97fcb

File tree

3 files changed

+29
-286
lines changed

3 files changed

+29
-286
lines changed

src/comp/middle/kind.rs

+27-284
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import std::option::some;
1+
import std::option::{some, none};
22
import syntax::{visit, ast_util};
33
import syntax::ast::*;
44
import syntax::codemap::span;
@@ -50,12 +50,33 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
5050
expr_bind(_, args) {
5151
for a in args { alt a { some(ex) { maybe_copy(cx, ex); } _ {} } }
5252
}
53-
// FIXME check for by-copy args
54-
expr_call(_f, _args, _) {
55-
53+
expr_call(f, args, _) {
54+
let i = 0u;
55+
for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) {
56+
alt arg_t.mode { by_copy. { maybe_copy(cx, args[i]); } _ {} }
57+
i += 1u;
58+
}
59+
}
60+
expr_path(_) {
61+
let substs = ty::node_id_to_ty_param_substs_opt_and_ty(cx.tcx, e.id);
62+
alt substs.substs {
63+
some(ts) {
64+
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
65+
let kinds = ty::lookup_item_type(cx.tcx, did).kinds, i = 0u;
66+
for ty in ts {
67+
let kind = ty::type_kind(cx.tcx, ty);
68+
if !ty::kind_lteq(kinds[i], kind) {
69+
cx.tcx.sess.span_err(e.span, "instantiating a " +
70+
kind_to_str(kinds[i]) +
71+
" type parameter with a "
72+
+ kind_to_str(kind) + " type");
73+
}
74+
i += 1u;
75+
}
76+
}
77+
none. {}
78+
}
5679
}
57-
// FIXME: generic instantiation
58-
expr_path(_) {}
5980
expr_fn({proto: proto_shared(_), _}) {
6081
for free in *freevars::get_freevars(cx.tcx, e.id) {
6182
let id = ast_util::def_id_of_def(free).node;
@@ -114,284 +135,6 @@ fn check_copy(cx: ctx, ty: ty::t, sp: span) {
114135
}
115136
}
116137

117-
118-
/*
119-
* Kinds are types of type.
120-
*
121-
* Every type has a kind. Every type parameter has a set of kind-capabilities
122-
* saying which kind of type may be passed as the parameter.
123-
*
124-
* The kinds are based on two capabilities: move and send. These may each be
125-
* present or absent, though only three of the four combinations can actually
126-
* occur:
127-
*
128-
*
129-
*
130-
* MOVE + SEND = "Unique": no shared substructures or pins, only
131-
* interiors and ~ boxes.
132-
*
133-
* MOVE + NOSEND = "Shared": structures containing @, fixed to the local
134-
* task heap/pool; or ~ structures pointing to
135-
* pinned values.
136-
*
137-
* NOMOVE + NOSEND = "Pinned": structures directly containing resources, or
138-
* by-alias closures as interior or
139-
* uniquely-boxed members.
140-
*
141-
* NOMOVE + SEND = -- : no types are like this.
142-
*
143-
*
144-
* Since this forms a lattice, we denote the capabilites in terms of a
145-
* worst-case requirement. That is, if your function needs to move-and-send (or
146-
* copy) your T, you write fn<uniq T>(...). If you need to move but not send,
147-
* you write fn<T>(...). And if you need neither -- can work with any sort of
148-
* pinned data at all -- then you write fn<pin T>(...).
149-
*
150-
* Most types are unique or shared. Other possible name combinations for these
151-
* two: (tree, graph; pruned, pooled; message, local; owned, common) are
152-
* plausible but nothing stands out as completely pithy-and-obvious.
153-
*
154-
* Pinned values arise in 2 contexts: resources and &-closures (blocks). The
155-
* latter absolutely must not be moved, since they could escape to the heap;
156-
* the former must not be copied, since they'd then be multiply-destructed.
157-
* We achieve the no-copy restriction by recycling the no-move restriction
158-
* in place on pinned kinds for &-closures; and as a benefit we can guarantee
159-
* that a resource passed by reference to C will never move during its life,
160-
* occasionally useful for FFI-code.
161-
*
162-
* Resources cannot be sent because we don't want to oblige the communication
163-
* system to run destructors in some weird limbo context of
164-
* messages-in-transit. It should always be ok to just free messages it's
165-
* dropping. Even if you wanted to send them, you'd need a new sigil for the
166-
* NOMOVE + SEND combination, and you couldn't use the move-mode library
167-
* interface to chan.send in that case (NOMOVE after all), so the whole thing
168-
* wouldn't really work as minimally as the encoding we have here.
169-
*
170-
* Note that obj~ and fn~ -- those that capture a unique environment -- can be
171-
* sent, so satisfy ~T. So can plain obj and fn. They can all also be copied.
172-
*
173-
* Further notes on copying and moving; sending is accomplished by calling a
174-
* move-in operator on something constrained to a unique type ~T.
175-
*
176-
*
177-
* COPYING:
178-
* --------
179-
*
180-
* A copy is made any time you pass-by-value or execute the = operator in a
181-
* non-init expression. Copying requires discriminating on type constructor.
182-
*
183-
* @-boxes copy shallow, copying is always legal.
184-
*
185-
* ~-boxes copy deep, copying is only legal if pointee is unique-kind.
186-
*
187-
* Pinned-kind values (resources, &-closures) can't be copied. All other
188-
* unique-kind (eg. interior) values can be copied, and copy shallow.
189-
*
190-
* Note: If you have no type constructor -- only an opaque typaram -- then
191-
* you can only copy if the typaram is constrained to ~T; this is because @T
192-
* might be a "~resource" box, and making a copy would cause a deep
193-
* resource-copy.
194-
*
195-
*
196-
* MOVING:
197-
* -------
198-
*
199-
* A move is made any time you pass-by-move (that is, with move mode '-') or
200-
* execute the move ('<-') or swap ('<->') operators.
201-
*
202-
*/
203-
204-
/*
205-
fn type_and_kind(tcx: ty::ctxt, e: @ast::expr) ->
206-
{ty: ty::t, kind: ast::kind} {
207-
let t = ty::expr_ty(tcx, e);
208-
let k = ty::type_kind(tcx, t);
209-
{ty: t, kind: k}
210-
}
211-
212-
fn need_expr_kind(tcx: ty::ctxt, e: @ast::expr, k_need: ast::kind,
213-
descr: str) {
214-
let tk = type_and_kind(tcx, e);
215-
log #fmt["for %s: want %s type, got %s type %s", descr,
216-
kind_to_str(k_need), kind_to_str(tk.kind),
217-
util::ppaux::ty_to_str(tcx, tk.ty)];
218-
219-
demand_kind(tcx, e.span, tk.ty, k_need, descr);
220-
}
221-
222-
fn demand_kind(tcx: ty::ctxt, sp: codemap::span, t: ty::t,
223-
k_need: ast::kind, descr: str) {
224-
let k = ty::type_kind(tcx, t);
225-
if !kind_lteq(k_need, k) {
226-
let s =
227-
#fmt["mismatched kinds for %s: needed %s type, got %s type %s",
228-
descr, kind_to_str(k_need), kind_to_str(k),
229-
util::ppaux::ty_to_str(tcx, t)];
230-
tcx.sess.span_err(sp, s);
231-
}
232-
}
233-
234-
fn need_shared_lhs_rhs(tcx: ty::ctxt, a: @ast::expr, b: @ast::expr, op: str) {
235-
need_expr_kind(tcx, a, ast::kind_copyable, op + " lhs");
236-
need_expr_kind(tcx, b, ast::kind_copyable, op + " rhs");
237-
}
238-
239-
/*
240-
This ... is a hack (I find myself writing that too often *sadface*).
241-
242-
We need to be able to put pinned kinds into other types but such operations
243-
are conceptually copies, and pinned kinds can't do that, e.g.
244-
245-
let a = my_resource(x);
246-
let b = @a; // no-go
247-
248-
So this function attempts to make a loophole where resources can be put into
249-
other types as long as it's done in a safe way, specifically like
250-
251-
let b = @my_resource(x);
252-
*/
253-
fn need_shared_or_pinned_ctor(tcx: ty::ctxt, a: @ast::expr, descr: str) {
254-
let tk = type_and_kind(tcx, a);
255-
if tk.kind == ast::kind_pinned && !pinned_ctor(a) {
256-
let err =
257-
#fmt["mismatched kinds for %s: cannot copy pinned type %s",
258-
descr, util::ppaux::ty_to_str(tcx, tk.ty)];
259-
tcx.sess.span_err(a.span, err);
260-
let note =
261-
#fmt["try constructing %s directly into %s",
262-
util::ppaux::ty_to_str(tcx, tk.ty), descr];
263-
tcx.sess.span_note(a.span, note);
264-
} else if tk.kind != ast::kind_pinned {
265-
need_expr_kind(tcx, a, ast::kind_shared, descr);
266-
}
267-
268-
fn pinned_ctor(a: @ast::expr) -> bool {
269-
// FIXME: Technically a lambda block is also a pinned ctor
270-
alt a.node {
271-
ast::expr_call(cexpr, _, _) {
272-
// Assuming that if it's a call that it's safe to move in, mostly
273-
// because I don't know offhand how to ensure that it's a call
274-
// specifically to a resource constructor
275-
true
276-
}
277-
ast::expr_rec(_, _) {
278-
true
279-
}
280-
ast::expr_unary(ast::uniq(_), _) {
281-
true
282-
}
283-
ast::expr_tup(_) {
284-
true
285-
}
286-
ast::expr_vec(exprs, _) {
287-
true
288-
}
289-
_ { false }
290-
}
291-
}
292-
}
293-
294-
fn check_expr(tcx: ty::ctxt, e: @ast::expr) {
295-
alt e.node {
296-
297-
// FIXME: These rules do not fully implement the copy type-constructor
298-
// discrimination described by the block comment at the top of this
299-
// file. This code is wrong; it lets you copy anything shared-kind.
300-
301-
ast::expr_move(a, b) { need_shared_lhs_rhs(tcx, a, b, "<-"); }
302-
ast::expr_assign(a, b) {
303-
need_shared_lhs_rhs(tcx, a, b, "=");
304-
}
305-
ast::expr_assign_op(_, a, b) {
306-
need_shared_lhs_rhs(tcx, a, b, "op=");
307-
}
308-
ast::expr_swap(a, b) { need_shared_lhs_rhs(tcx, a, b, "<->"); }
309-
ast::expr_copy(a) {
310-
need_expr_kind(tcx, a, ast::kind_shared, "'copy' operand");
311-
}
312-
ast::expr_ret(option::some(a)) {
313-
need_expr_kind(tcx, a, ast::kind_shared, "'ret' operand");
314-
}
315-
ast::expr_be(a) {
316-
need_expr_kind(tcx, a, ast::kind_shared, "'be' operand");
317-
}
318-
ast::expr_fail(option::some(a)) {
319-
need_expr_kind(tcx, a, ast::kind_shared, "'fail' operand");
320-
}
321-
ast::expr_call(callee, _, _) {
322-
let tpt = ty::expr_ty_params_and_ty(tcx, callee);
323-
324-
// If we have typarams, we're calling an item; we need to check
325-
// that all the types we're supplying as typarams conform to the
326-
// typaram kind constraints on that item.
327-
if vec::len(tpt.params) != 0u {
328-
let callee_def =
329-
ast_util::def_id_of_def(tcx.def_map.get(callee.id));
330-
let item_tk = ty::lookup_item_type(tcx, callee_def);
331-
let i = 0;
332-
assert (vec::len(item_tk.kinds) == vec::len(tpt.params));
333-
for k_need: ast::kind in item_tk.kinds {
334-
let t = tpt.params[i];
335-
demand_kind(tcx, e.span, t, k_need,
336-
#fmt("typaram %d", i));
337-
i += 1;
338-
}
339-
}
340-
}
341-
ast::expr_unary(op, a) {
342-
alt op {
343-
ast::box(_) {
344-
need_shared_or_pinned_ctor(tcx, a, "'@' operand");
345-
}
346-
ast::uniq(_) {
347-
need_shared_or_pinned_ctor(tcx, a, "'~' operand");
348-
}
349-
_ { /* fall through */ }
350-
}
351-
}
352-
ast::expr_rec(fields, _) {
353-
for field in fields {
354-
need_shared_or_pinned_ctor(tcx, field.node.expr, "record field");
355-
}
356-
}
357-
ast::expr_tup(exprs) {
358-
for expr in exprs {
359-
need_shared_or_pinned_ctor(tcx, expr, "tuple parameter");
360-
}
361-
}
362-
ast::expr_vec(exprs, _) {
363-
// Putting pinned things into vectors is pretty useless since vector
364-
// addition can't work (it's a copy)
365-
for expr in exprs {
366-
need_expr_kind(tcx, expr, ast::kind_shared, "vector element");
367-
}
368-
}
369-
_ { }
370-
}
371-
}
372-
373-
fn check_stmt(tcx: ty::ctxt, stmt: @ast::stmt) {
374-
alt stmt.node {
375-
ast::stmt_decl(@{node: ast::decl_local(locals), _}, _) {
376-
for (let_style, local) in locals {
377-
alt local.node.init {
378-
option::some({op: ast::init_assign., expr}) {
379-
need_shared_or_pinned_ctor(tcx, expr,
380-
"local initializer");
381-
}
382-
option::some({op: ast::init_move., expr}) {
383-
need_shared_or_pinned_ctor(tcx, expr,
384-
"local initializer");
385-
}
386-
option::none. { /* fall through */ }
387-
}
388-
}
389-
}
390-
_ { /* fall through */ }
391-
}
392-
}
393-
*/
394-
395138
//
396139
// Local Variables:
397140
// mode: rust

src/comp/middle/trans.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4079,7 +4079,7 @@ fn trans_expr_save_in(bcx: @block_ctxt, e: @ast::expr, dest: ValueRef)
40794079

40804080
// Call this to compile an expression that you need as an intermediate value,
40814081
// and you want to know whether you're dealing with an lval or not (the kind
4082-
// field in the returned struct). For non-immediates, use trans_expr or
4082+
// field in the returned struct). For non-intermediates, use trans_expr or
40834083
// trans_expr_save_in. For intermediates where you don't care about lval-ness,
40844084
// use trans_temp_expr.
40854085
fn trans_temp_lval(bcx: @block_ctxt, e: @ast::expr) -> lval_result {

src/comp/middle/ty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,7 @@ fn type_allows_implicit_copy(cx: ctxt, ty: t) -> bool {
11431143
}
11441144
_ { false }
11451145
};
1146-
}) && type_kind(cx, t) != ast::kind_noncopyable;
1146+
}) && type_kind(cx, ty) != ast::kind_noncopyable;
11471147
}
11481148

11491149
fn type_structurally_contains_uniques(cx: ctxt, ty: t) -> bool {

0 commit comments

Comments
 (0)