diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index 3dacbd3b9d0..b267af517b4 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -11,6 +11,7 @@ import front::attr; import middle::trans; import middle::resolve; import middle::freevars; +import middle::kind; import middle::ty; import middle::typeck; import middle::tstate::ck; @@ -147,6 +148,8 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str, } time(time_passes, "alias checking", bind middle::alias::check_crate(ty_cx, crate)); + time[()](time_passes, "kind checking", + bind kind::check_crate(ty_cx, crate)); let llmod = time[llvm::llvm::ModuleRef](time_passes, "translation", bind trans::trans_crate(sess, crate, diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs new file mode 100644 index 00000000000..1d391f80f84 --- /dev/null +++ b/src/comp/middle/kind.rs @@ -0,0 +1,128 @@ +/* +* Kinds are types of type. +* +* Every type has a kind. Every type parameter has a set of kind-capabilities +* saying which kind of type may be passed as the parameter. +* +* The kinds are based on two capabilities: copy and send. These may each be +* present or absent, though only three of the four combinations can actually +* occur: +* +* +* +* COPY + SEND = "Unique": no shared substructures or pins, only +* interiors and ~ boxes. +* +* COPY + NOSEND = "Shared": structures containing @, fixed to the local +* task heap/pool. +* +* NOCOPY + NOSEND = "Pinned": structures containing resources or +* by-alias closures as interior or +* uniquely-boxed members. +* +* NOCOPY + SEND = -- : no types are like this. +* +* +* Since this forms a lattice, we denote the capabilites in terms of a +* worst-case requirement. That is, if your function needs to copy-and-send +* your T, you write fn<~T>(...). If you need to copy but not send, you write +* fn<@T>(...). And if you need neither -- can work with any sort of pinned +* data at all -- then you write fn(...). +* +* +* Most types are unique or shared. Other possible name combinations for these +* two: (tree, graph; pruned, pooled; message, local; owned, common) are +* plausible but nothing stands out as completely pithy-and-obvious. +* +* Resources cannot be copied or sent; they're pinned. They can't be copied +* because it would interfere with destruction (multiple destruction?) They +* cannot be sent because we don't want to oblige the communication system to +* run destructors in some weird limbo context of messages-in-transit. It +* should always be ok to just free messages it's dropping. +* +* Note that obj~ and fn~ -- those that capture a unique environment -- can be +* sent, so satisfy ~T. So can plain obj and fn. +* +* +* Further notes on copying and moving; sending is accomplished by calling a +* move-in operator on something constrained to a unique type ~T. +* +* +* COPYING: +* -------- +* +* A copy is made any time you pass-by-value or execute the = operator in a +* non-init expression. +* +* ~ copies deep +* @ copies shallow +* pinned values (pinned resources, alias-closures) can't be copied +* all other interiors copy shallow +* +* MOVING: +* ------- +* +* A move is made any time you pass-by-move (that is, with 'move' mode) or +* execute the <- operator. +* +* Anything you can copy, you can move. Move is (semantically) just +* shallow-copy + deinit. Note that: ~ moves shallow even though it copies +* deep. Move is the operator that lets ~ copy shallow: by pairing it with a +* deinit. +* +*/ + + +import syntax::ast; +import syntax::walk; + +import ast::kind; +import ast::kind_unique; +import ast::kind_shared; +import ast::kind_pinned; + +fn kind_lteq(a: kind, b: kind) -> bool { + alt a { + kind_pinned. { true } + kind_shared. { b != kind_pinned } + kind_unique. { b == kind_unique } + } +} + +fn lower_kind(a: kind, b: kind) -> kind { + if kind_lteq(a, b) { a } else { b } +} + +fn kind_to_str(k: kind) -> str { + alt k { + ast::kind_pinned. { "pinned" } + ast::kind_unique. { "unique" } + ast::kind_shared. { "shared" } + } +} + +fn check_expr(tcx: &ty::ctxt, e: &@ast::expr) { + let t = ty::expr_ty(tcx, e); + let k = ty::type_kind(tcx, t); + log #fmt("%s type: %s", kind_to_str(k), + util::ppaux::ty_to_str(tcx, t)); +} + +fn check_crate(tcx: &ty::ctxt, crate: &@ast::crate) { + let visit = + {visit_expr_pre: bind check_expr(tcx, _) + with walk::default_visitor()}; + walk::walk_crate(visit, *crate); + tcx.sess.abort_if_errors(); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 674e5c9b9cb..08e6447c2d0 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -152,6 +152,7 @@ export ty_fn_args; export type_constr; export type_contains_params; export type_contains_vars; +export type_kind; export type_err; export type_err_to_str; export type_has_dynamic_size; @@ -216,6 +217,7 @@ type ctxt = rcache: creader_cache, short_names_cache: hashmap[t, str], has_pointer_cache: hashmap[t, bool], + kind_cache: hashmap[t, ast::kind], owns_heap_mem_cache: hashmap[t, bool], ast_ty_to_ty_cache: hashmap[@ast::ty, option::t[t]]}; @@ -409,6 +411,7 @@ fn mk_ctxt(s: session::session, dm: resolve::def_map, amap: ast_map::map, rcache: mk_rcache(), short_names_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty), has_pointer_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty), + kind_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty), owns_heap_mem_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty), ast_ty_to_ty_cache: map::mk_hashmap(ast::hash_ty, ast::eq_ty)}; populate_type_store(cx); @@ -981,7 +984,10 @@ fn type_has_pointers(cx: &ctxt, ty: &t) -> bool { ty_native(_) {/* no-op */ } ty_rec(flds) { for f: field in flds { - if type_has_pointers(cx, f.mt.ty) { result = true; } + if type_has_pointers(cx, f.mt.ty) { + result = true; + break; + } } } ty_tag(did, tps) { @@ -990,8 +996,12 @@ fn type_has_pointers(cx: &ctxt, ty: &t) -> bool { for aty: t in variant.args { // Perform any type parameter substitutions. let arg_ty = substitute_type_params(cx, tps, aty); - if type_has_pointers(cx, arg_ty) { result = true; } + if type_has_pointers(cx, arg_ty) { + result = true; + break; + } } + if result { break; } } } ty_res(did, inner, tps) { @@ -1005,6 +1015,122 @@ fn type_has_pointers(cx: &ctxt, ty: &t) -> bool { ret result; } +fn type_kind(cx: &ctxt, ty: &t) -> ast::kind { + alt cx.kind_cache.find(ty) { + some(result) { ret result; } + none. {/* fall through */ } + } + + let result = ast::kind_unique; + + // Insert a default in case we loop back on self recursively. + cx.kind_cache.insert(ty, result); + + alt struct(cx, ty) { + + // Scalar types are unique-kind, no substructure. + ty_nil. | ty_bot. | ty_bool. | ty_int. | ty_uint. | ty_float. + | ty_machine(_) | ty_char. | ty_native(_) { + // no-op + } + + // A handful of other built-in are unique too. + ty_type. | ty_istr. | ty_native_fn(_, _, _) { + // no-op + } + + // Those things with refcounts-to-interior are just shared. + ty_str. | ty_task. { + result = kind_shared; + } + + // FIXME: obj is broken for now, since we aren't asserting + // anything about its fields. + ty_obj(_) { result = kind_shared; } + + // FIXME: the environment capture mode is not fully encoded + // here yet, leading to weirdness around closure. + ty_fn(proto, _, _, _, _) { + result = alt proto { + ast::proto_block. { ast::kind_pinned } + ast::proto_closure. { ast::kind_shared } + _ { ast::kind_unique } + } + } + + // Those with refcounts-to-inner are the lower of their + // inner and shared. + ty_box(mt) | ty_vec(mt) { + result = kind::lower_kind(ast::kind_shared, + type_kind(cx, mt.ty)); + + } + + // FIXME: remove ports. Ports currently contribute 'shared' + ty_port(t) { + result = kind::lower_kind(ast::kind_shared, + type_kind(cx, t)); + } + + // FIXME: remove chans. Chans currently contribute only + // their inner. + ty_chan(t) { + result = type_kind(cx, t); + } + + // Pointers and unique boxes / vecs lower to whatever they point to. + ty_ptr(tm) | ty_ivec(tm) { + result = type_kind(cx, tm.ty); + } + + // Records lower to the lowest of their members. + ty_rec(flds) { + for f: field in flds { + result = kind::lower_kind(result, type_kind(cx, f.mt.ty)); + if result == ast::kind_pinned { break; } + } + } + + // Tags lower to the lowest of their variants. + ty_tag(did, tps) { + let variants = tag_variants(cx, did); + for variant: variant_info in variants { + for aty: t in variant.args { + // Perform any type parameter substitutions. + let arg_ty = substitute_type_params(cx, tps, aty); + result = kind::lower_kind(result, type_kind(cx, arg_ty)); + if result == ast::kind_pinned { break; } + } + if result == ast::kind_pinned { break; } + } + } + + // Resources are always pinned. + ty_res(did, inner, tps) { + result = ast::kind_pinned; + } + + ty_var(_) { fail; } + + ty_param(_) { + // FIXME: this should contribute the kind-bound of the typaram, + // when those exist. + } + + ty_constr(t, _) { + result = type_kind(cx, t); + } + + _ { + cx.sess.bug("missed case: " + ty_to_str(cx, ty)); + } + + } + + cx.kind_cache.insert(ty, result); + ret result; +} + // FIXME: should we just return true for native types in // type_is_scalar? diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 7c9377bd19d..da6f7e5c853 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -27,6 +27,7 @@ mod middle { mod typeck; mod check_alt; mod alias; + mod kind; mod freevars; mod tstate { diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index 14375e5c73b..45be817f91a 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -157,7 +157,7 @@ fn pat_id_map(pat: &@pat) -> pat_id_map { tag mutability { mut; imm; maybe_mut; } -tag layer { layer_value; layer_state; layer_gc; } +tag kind { kind_pinned; kind_shared; kind_unique; } tag _auth { auth_unsafe; } diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 1782c826785..4d8fe833559 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -483,7 +483,6 @@ fn parse_ty(p: &parser) -> @ast::ty { let t: ast::ty_; // FIXME: do something with this - parse_layer(p); if eat_word(p, "bool") { t = ast::ty_bool; } else if (eat_word(p, "int")) { @@ -1821,7 +1820,7 @@ fn parse_dtor(p: &parser) -> @ast::method { ret @spanned(lo, f.body.span.hi, m); } -fn parse_item_obj(p: &parser, lyr: ast::layer, attrs: &ast::attribute[]) -> +fn parse_item_obj(p: &parser, attrs: &ast::attribute[]) -> @ast::item { let lo = p.get_last_lo_pos(); let ident = parse_value_ident(p); @@ -1844,7 +1843,7 @@ fn parse_item_obj(p: &parser, lyr: ast::layer, attrs: &ast::attribute[]) -> attrs); } -fn parse_item_res(p: &parser, lyr: ast::layer, attrs: &ast::attribute[]) -> +fn parse_item_res(p: &parser, attrs: &ast::attribute[]) -> @ast::item { let lo = p.get_last_lo_pos(); let ident = parse_value_ident(p); @@ -1945,7 +1944,6 @@ fn parse_item_native_fn(p: &parser, attrs: &ast::attribute[]) -> fn parse_native_item(p: &parser, attrs: &ast::attribute[]) -> @ast::native_item { - parse_layer(p); if eat_word(p, "type") { ret parse_item_native_type(p, attrs); } else if (eat_word(p, "fn")) { @@ -2084,15 +2082,6 @@ fn parse_item_tag(p: &parser, attrs: &ast::attribute[]) -> @ast::item { ret mk_item(p, lo, hi, id, ast::item_tag(variants, ty_params), attrs); } -fn parse_layer(p: &parser) -> ast::layer { - if eat_word(p, "state") { - ret ast::layer_state; - } else if (eat_word(p, "gc")) { - ret ast::layer_gc; - } else { ret ast::layer_value; } - fail; -} - fn parse_auth(p: &parser) -> ast::_auth { if eat_word(p, "unsafe") { ret ast::auth_unsafe; @@ -2122,15 +2111,14 @@ fn parse_item(p: &parser, attrs: &ast::attribute[]) -> parsed_item { } else if (eat_word(p, "native")) { ret got_item(parse_item_native_mod(p, attrs)); } - let lyr = parse_layer(p); if eat_word(p, "type") { ret got_item(parse_item_type(p, attrs)); } else if (eat_word(p, "tag")) { ret got_item(parse_item_tag(p, attrs)); } else if (eat_word(p, "obj")) { - ret got_item(parse_item_obj(p, lyr, attrs)); + ret got_item(parse_item_obj(p, attrs)); } else if (eat_word(p, "resource")) { - ret got_item(parse_item_res(p, lyr, attrs)); + ret got_item(parse_item_res(p, attrs)); } else { ret no_item; } }