prevent regions from escaping in ifaces; remove &r.T syntax

This commit is contained in:
Niko Matsakis 2012-07-18 11:01:54 -07:00
parent eb0a34c398
commit e0ea67a2a6
36 changed files with 411 additions and 130 deletions

View file

@ -832,10 +832,10 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
* types; arbitrary type coercion is possible this way. The interface is safe
* as long as all key functions are monomorphic.
*/
type local_data_key<T> = fn@(+@T);
type local_data_key<T: owned> = fn@(+@T);
iface local_data { }
impl<T> of local_data for @T { }
impl<T: owned> of local_data for @T { }
// We use dvec because it's the best data structure in core. If TLS is used
// heavily in future, this could be made more efficient with a proper map.
@ -852,6 +852,7 @@ extern fn cleanup_task_local_map(map_ptr: *libc::c_void) unsafe {
// Gets the map from the runtime. Lazily initialises if not done so already.
unsafe fn get_task_local_map(task: *rust_task) -> task_local_map {
// Relies on the runtime initialising the pointer to null.
// NOTE: The map's box lives in TLS invisibly referenced once. Each time
// we retrieve it for get/set, we make another reference, which get/set
@ -872,7 +873,9 @@ unsafe fn get_task_local_map(task: *rust_task) -> task_local_map {
}
}
unsafe fn key_to_key_value<T>(key: local_data_key<T>) -> *libc::c_void {
unsafe fn key_to_key_value<T: owned>(
key: local_data_key<T>) -> *libc::c_void {
// Keys are closures, which are (fnptr,envptr) pairs. Use fnptr.
// Use reintepret_cast -- transmute would leak (forget) the closure.
let pair: (*libc::c_void, *libc::c_void) = unsafe::reinterpret_cast(key);
@ -880,8 +883,10 @@ unsafe fn key_to_key_value<T>(key: local_data_key<T>) -> *libc::c_void {
}
// If returning some(..), returns with @T with the map's reference. Careful!
unsafe fn local_data_lookup<T>(map: task_local_map, key: local_data_key<T>)
unsafe fn local_data_lookup<T: owned>(
map: task_local_map, key: local_data_key<T>)
-> option<(uint, *libc::c_void)> {
let key_value = key_to_key_value(key);
let map_pos = (*map).position(|entry|
alt entry { some((k,_,_)) { k == key_value } none { false } }
@ -893,8 +898,10 @@ unsafe fn local_data_lookup<T>(map: task_local_map, key: local_data_key<T>)
}
}
unsafe fn local_get_helper<T>(task: *rust_task, key: local_data_key<T>,
unsafe fn local_get_helper<T: owned>(
task: *rust_task, key: local_data_key<T>,
do_pop: bool) -> option<@T> {
let map = get_task_local_map(task);
// Interpret our findings from the map
do local_data_lookup(map, key).map |result| {
@ -912,17 +919,23 @@ unsafe fn local_get_helper<T>(task: *rust_task, key: local_data_key<T>,
}
}
unsafe fn local_pop<T>(task: *rust_task,
unsafe fn local_pop<T: owned>(
task: *rust_task,
key: local_data_key<T>) -> option<@T> {
local_get_helper(task, key, true)
}
unsafe fn local_get<T>(task: *rust_task,
unsafe fn local_get<T: owned>(
task: *rust_task,
key: local_data_key<T>) -> option<@T> {
local_get_helper(task, key, false)
}
unsafe fn local_set<T>(task: *rust_task, key: local_data_key<T>, +data: @T) {
unsafe fn local_set<T: owned>(
task: *rust_task, key: local_data_key<T>, +data: @T) {
let map = get_task_local_map(task);
// Store key+data as *voids. Data is invisibly referenced once; key isn't.
let keyval = key_to_key_value(key);
@ -956,8 +969,10 @@ unsafe fn local_set<T>(task: *rust_task, key: local_data_key<T>, +data: @T) {
}
}
unsafe fn local_modify<T>(task: *rust_task, key: local_data_key<T>,
unsafe fn local_modify<T: owned>(
task: *rust_task, key: local_data_key<T>,
modify_fn: fn(option<@T>) -> option<@T>) {
// Could be more efficient by doing the lookup work, but this is easy.
let newdata = modify_fn(local_pop(task, key));
if newdata.is_some() {
@ -970,29 +985,37 @@ unsafe fn local_modify<T>(task: *rust_task, key: local_data_key<T>,
* Remove a task-local data value from the table, returning the
* reference that was originally created to insert it.
*/
unsafe fn local_data_pop<T>(key: local_data_key<T>) -> option<@T> {
unsafe fn local_data_pop<T: owned>(
key: local_data_key<T>) -> option<@T> {
local_pop(rustrt::rust_get_task(), key)
}
/**
* Retrieve a task-local data value. It will also be kept alive in the
* table until explicitly removed.
*/
unsafe fn local_data_get<T>(key: local_data_key<T>) -> option<@T> {
unsafe fn local_data_get<T: owned>(
key: local_data_key<T>) -> option<@T> {
local_get(rustrt::rust_get_task(), key)
}
/**
* Store a value in task-local data. If this key already has a value,
* that value is overwritten (and its destructor is run).
*/
unsafe fn local_data_set<T>(key: local_data_key<T>, +data: @T) {
unsafe fn local_data_set<T: owned>(
key: local_data_key<T>, +data: @T) {
local_set(rustrt::rust_get_task(), key, data)
}
/**
* Modify a task-local data value. If the function returns 'none', the
* data is removed (and its reference dropped).
*/
unsafe fn local_data_modify<T>(key: local_data_key<T>,
unsafe fn local_data_modify<T: owned>(
key: local_data_key<T>,
modify_fn: fn(option<@T>) -> option<@T>) {
local_modify(rustrt::rust_get_task(), key, modify_fn)
}

View file

@ -193,7 +193,9 @@ mod tests {
type eqfn<T> = fn@(T, T) -> bool;
fn test_parameterized<T: copy>(e: eqfn<T>, a: T, b: T, c: T, d: T) {
fn test_parameterized<T: copy owned>(
e: eqfn<T>, a: T, b: T, c: T, d: T) {
let deq: deque::t<T> = deque::create::<T>();
assert (deq.size() == 0u);
deq.add_front(a);

View file

@ -383,8 +383,7 @@ class parser {
let name =
alt copy self.token {
token::IDENT(sid, _) => {
if self.look_ahead(1u) == token::DOT || // backwards compat
self.look_ahead(1u) == token::BINOP(token::SLASH) {
if self.look_ahead(1u) == token::BINOP(token::SLASH) {
self.bump(); self.bump();
some(self.get_str(sid))
} else {

View file

@ -345,7 +345,7 @@ fn print_type_ex(s: ps, &&ty: @ast::ty, print_colons: bool) {
ast::ty_rptr(region, mt) {
alt region.node {
ast::re_anon { word(s.s, ~"&"); }
_ { print_region(s, region); word(s.s, ~"."); }
_ { print_region(s, region); word(s.s, ~"/"); }
}
print_mt(s, mt);
}

View file

@ -272,7 +272,7 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
w.write_char('I');
w.write_uint(id.to_uint());
}
ty::ty_param(id, did) {
ty::ty_param({idx: id, def_id: did}) {
w.write_char('p');
w.write_str(cx.ds(did));
w.write_char('|');

View file

@ -115,7 +115,7 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
fn check_for_box(cx: ctx, id: node_id, fv: option<@freevar_entry>,
is_move: bool, var_t: ty::t, sp: span) {
// all captured data must be owned
if !check_owned(cx, var_t, sp) { ret; }
if !check_owned(cx.tcx, var_t, sp) { ret; }
// copied in data must be copyable, but moved in data can be anything
let is_implicit = fv.is_some();
@ -217,7 +217,13 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
alt e.node {
expr_assign(_, ex) |
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
expr_ret(some(ex)) | expr_cast(ex, _) { maybe_copy(cx, ex); }
expr_ret(some(ex)) {
maybe_copy(cx, ex);
}
expr_cast(source, _) {
maybe_copy(cx, source);
check_cast_for_escaping_regions(cx, source, e);
}
expr_copy(expr) { check_copy_ex(cx, expr, false); }
// Vector add copies, but not "implicitly"
expr_assign_op(_, _, ex) { check_copy_ex(cx, ex, false) }
@ -440,15 +446,92 @@ fn check_send(cx: ctx, ty: ty::t, sp: span) -> bool {
}
}
fn check_owned(cx: ctx, ty: ty::t, sp: span) -> bool {
if !ty::kind_is_owned(ty::type_kind(cx.tcx, ty)) {
cx.tcx.sess.span_err(sp, ~"not an owned value");
// note: also used from middle::typeck::regionck!
fn check_owned(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool {
if !ty::kind_is_owned(ty::type_kind(tcx, ty)) {
alt ty::get(ty).struct {
ty::ty_param(*) {
tcx.sess.span_err(sp, ~"value may contain borrowed \
pointers; use `owned` bound");
}
_ {
tcx.sess.span_err(sp, ~"value may contain borrowed \
pointers");
}
}
false
} else {
true
}
}
/// This is rather subtle. When we are casting a value to a
/// instantiated iface like `a as iface/&r`, regionck already ensures
/// that any borrowed pointers that appear in the type of `a` are
/// bounded by `&r`. However, it is possible that there are *type
/// parameters* in the type of `a`, and those *type parameters* may
/// have borrowed pointers within them. We have to guarantee that the
/// regions which appear in those type parameters are not obscured.
///
/// Therefore, we ensure that one of three conditions holds:
///
/// (1) The iface instance cannot escape the current fn. This is
/// guaranteed if the region bound `&r` is some scope within the fn
/// itself. This case is safe because whatever borrowed pointers are
/// found within the type parameter, they must enclose the fn body
/// itself.
///
/// (2) The type parameter appears in the type of the iface. For
/// example, if the type parameter is `T` and the iface type is
/// `deque<T>`, then whatever borrowed ptrs may appear in `T` also
/// appear in `deque<T>`.
///
/// (3) The type parameter is owned (and therefore does not contain
/// borrowed ptrs).
fn check_cast_for_escaping_regions(
cx: ctx,
source: @expr,
target: @expr) {
// Determine what type we are casting to; if it is not an iface, then no
// worries.
let target_ty = ty::expr_ty(cx.tcx, target);
let target_substs = alt ty::get(target_ty).struct {
ty::ty_trait(_, substs) => {substs}
_ => { ret; /* not a cast to a trait */ }
};
// Check, based on the region associated with the iface, whether it can
// possibly escape the enclosing fn item (note that all type parameters
// must have been declared on the enclosing fn item):
alt target_substs.self_r {
some(ty::re_scope(*)) => { ret; /* case (1) */ }
none | some(ty::re_static) | some(ty::re_free(*)) => {}
some(ty::re_bound(*)) | some(ty::re_var(*)) => {
cx.tcx.sess.span_bug(
source.span,
#fmt["bad region found in kind: %?", target_substs.self_r]);
}
}
// Assuming the iface instance can escape, then ensure that each parameter
// either appears in the iface type or is owned:
let target_params = ty::param_tys_in_type(target_ty);
let source_ty = ty::expr_ty(cx.tcx, source);
do ty::walk_ty(source_ty) |ty| {
alt ty::get(ty).struct {
ty::ty_param(source_param) => {
if target_params.contains(source_param) {
/* case (2) */
} else {
check_owned(cx.tcx, ty, source.span); /* case (3) */
}
}
_ => {}
}
}
}
//
// Local Variables:
// mode: rust

View file

@ -1317,7 +1317,7 @@ enum copy_action { INIT, DROP_EXISTING, }
fn type_is_structural_or_param(t: ty::t) -> bool {
if ty::type_is_structural(t) { ret true; }
alt ty::get(t).struct {
ty::ty_param(_, _) { ret true; }
ty::ty_param(*) { ret true; }
_ { ret false; }
}
}

View file

@ -272,7 +272,7 @@ impl methods for reflector {
ty::ty_trait(_, _) { self.leaf(~"trait") }
ty::ty_var(_) { self.leaf(~"var") }
ty::ty_var_integral(_) { self.leaf(~"var_integral") }
ty::ty_param(n, _) { self.visit(~"param", ~[self.c_uint(n)]) }
ty::ty_param(p) { self.visit(~"param", ~[self.c_uint(p.idx)]) }
ty::ty_self { self.leaf(~"self") }
ty::ty_type { self.leaf(~"type") }
ty::ty_opaque_box { self.leaf(~"opaque_box") }

View file

@ -137,8 +137,8 @@ fn type_needs_inner(cx: ctx, use: uint, ty: ty::t,
}
false
}
ty::ty_param(n, _) {
cx.uses[n] |= use;
ty::ty_param(p) {
cx.uses[p.idx] |= use;
false
}
_ { true }

View file

@ -166,6 +166,7 @@ export terr_regions_differ, terr_mutability, terr_purity_mismatch;
export terr_proto_mismatch;
export terr_ret_style_mismatch;
export purity_to_str;
export param_tys_in_type;
// Data types
@ -312,6 +313,8 @@ type fn_ty = {purity: ast::purity,
output: t,
ret_style: ret_style};
type param_ty = {idx: uint, def_id: def_id};
// See discussion at head of region.rs
enum region {
re_bound(bound_region),
@ -370,7 +373,7 @@ enum sty {
ty_var(tv_vid), // type variable during typechecking
ty_var_integral(tvi_vid), // type variable during typechecking, for
// integral types only
ty_param(uint, def_id), // type parameter
ty_param(param_ty), // type parameter
ty_self, // special, implicit `self` type parameter
// "Fake" types, used for trans purposes
@ -579,7 +582,7 @@ fn mk_t_with_id(cx: ctxt, st: sty, o_def_id: option<ast::def_id>) -> t {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) |
ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) |
ty_opaque_box {}
ty_param(_, _) { flags |= has_params as uint; }
ty_param(_) { flags |= has_params as uint; }
ty_var(_) | ty_var_integral(_) { flags |= needs_infer as uint; }
ty_self { flags |= has_self as uint; }
ty_enum(_, substs) | ty_class(_, substs) | ty_trait(_, substs) {
@ -713,7 +716,9 @@ fn mk_var_integral(cx: ctxt, v: tvi_vid) -> t {
fn mk_self(cx: ctxt) -> t { mk_t(cx, ty_self) }
fn mk_param(cx: ctxt, n: uint, k: def_id) -> t { mk_t(cx, ty_param(n, k)) }
fn mk_param(cx: ctxt, n: uint, k: def_id) -> t {
mk_t(cx, ty_param({idx: n, def_id: k}))
}
fn mk_type(cx: ctxt) -> t { mk_t(cx, ty_type) }
@ -761,7 +766,7 @@ fn maybe_walk_ty(ty: t, f: fn(t) -> bool) {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_estr(_) | ty_type | ty_opaque_box | ty_self |
ty_opaque_closure_ptr(_) | ty_var(_) | ty_var_integral(_) |
ty_param(_, _) {
ty_param(_) {
}
ty_box(tm) | ty_evec(tm, _) | ty_unboxed_vec(tm) |
ty_ptr(tm) | ty_rptr(_, tm) {
@ -982,7 +987,7 @@ fn subst_tps(cx: ctxt, tps: ~[t], typ: t) -> t {
let tb = ty::get(typ);
if !tbox_has_flag(tb, has_params) { ret typ; }
alt tb.struct {
ty_param(idx, _) { tps[idx] }
ty_param(p) { tps[p.idx] }
sty { fold_sty_to_ty(cx, sty, |t| subst_tps(cx, tps, t)) }
}
}
@ -1019,7 +1024,7 @@ fn subst(cx: ctxt,
let tb = get(typ);
if !tbox_has_flag(tb, needs_subst) { ret typ; }
alt tb.struct {
ty_param(idx, _) {substs.tps[idx]}
ty_param(p) {substs.tps[p.idx]}
ty_self {substs.self_ty.get()}
_ {
fold_regions_and_ty(
@ -1608,8 +1613,8 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
lowest
}
ty_param(_, did) {
param_bounds_to_kind(cx.ty_param_bounds.get(did.node))
ty_param(p) {
param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node))
}
// self is a special type parameter that can only appear in ifaces; it
@ -1666,7 +1671,7 @@ fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
ty_fn(_) |
ty_var(_) |
ty_var_integral(_) |
ty_param(_, _) |
ty_param(_) |
ty_self |
ty_type |
ty_opaque_box |
@ -1855,7 +1860,7 @@ fn type_is_pod(cx: ctxt, ty: t) -> bool {
ty_evec(mt, vstore_fixed(_)) | ty_unboxed_vec(mt) {
result = type_is_pod(cx, mt.ty);
}
ty_param(_, _) { result = false; }
ty_param(_) { result = false; }
ty_opaque_closure_ptr(_) { result = true; }
ty_class(did, substs) {
result = vec::any(lookup_class_fields(cx, did), |f| {
@ -1899,7 +1904,7 @@ fn type_is_c_like_enum(cx: ctxt, ty: t) -> bool {
fn type_param(ty: t) -> option<uint> {
alt get(ty).struct {
ty_param(id, _) { ret some(id); }
ty_param(p) { ret some(p.idx); }
_ {/* fall through */ }
}
ret none;
@ -2038,7 +2043,7 @@ fn hash_type_structure(st: sty) -> uint {
ty_self { 28u }
ty_var(v) { hash_uint(29u, v.to_uint()) }
ty_var_integral(v) { hash_uint(30u, v.to_uint()) }
ty_param(pid, did) { hash_def(hash_uint(31u, pid), did) }
ty_param(p) { hash_def(hash_uint(31u, p.idx), p.def_id) }
ty_type { 32u }
ty_bot { 34u }
ty_ptr(mt) { hash_subty(35u, mt.ty) }
@ -2219,6 +2224,22 @@ fn method_idx(id: ast::ident, meths: ~[method]) -> option<uint> {
ret none;
}
/// Returns a vector containing the indices of all type parameters that appear
/// in `ty`. The vector may contain duplicates. Probably should be converted
/// to a bitset or some other representation.
fn param_tys_in_type(ty: t) -> ~[param_ty] {
let mut rslt = ~[];
do walk_ty(ty) |ty| {
alt get(ty).struct {
ty_param(p) {
vec::push(rslt, p);
}
_ { }
}
}
rslt
}
fn occurs_check(tcx: ctxt, sp: span, vid: tv_vid, rt: t) {
// Returns a vec of all the type variables occurring in `ty`. It may
@ -2341,7 +2362,7 @@ fn ty_sort_str(cx: ctxt, t: t) -> ~str {
ty_tup(_) { ~"tuple" }
ty_var(_) { ~"variable" }
ty_var_integral(_) { ~"integral variable" }
ty_param(_, _) { ~"type parameter" }
ty_param(_) { ~"type parameter" }
ty_self { ~"self" }
}
}

View file

@ -68,7 +68,7 @@ fn get_region_reporting_err(tcx: ty::ctxt,
}
}
fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
fn ast_region_to_region<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region {
let res = alt a_r.node {
@ -79,7 +79,7 @@ fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
get_region_reporting_err(self.tcx(), span, res)
}
fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, did: ast::def_id,
path: @ast::path) -> ty_param_substs_and_ty {
@ -128,7 +128,7 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
{substs: substs, ty: ty::subst(tcx, substs, decl_ty)}
}
fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy>(
fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy owned>(
self: AC,
rscope: RS,
did: ast::def_id,
@ -151,10 +151,10 @@ const NO_TPS: uint = 2u;
// Parses the programmer's textual representation of a type into our
// internal notion of a type. `getter` is a function that returns the type
// corresponding to a definition ID:
fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, mt: ast::mt) -> ty::mt {
ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl};
@ -162,7 +162,7 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
// Handle @, ~, and & being able to mean estrs and evecs.
// If a_seq_ty is a str or a vec, make it an estr/evec
fn mk_maybe_vstore<AC: ast_conv, RS: region_scope copy>(
fn mk_maybe_vstore<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, a_seq_ty: ast::mt, vst: ty::vstore,
constr: fn(ty::mt) -> ty::t) -> ty::t {
@ -351,7 +351,7 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
ret typ;
}
fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
fn ty_of_arg<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS, a: ast::arg,
expected_ty: option<ty::arg>) -> ty::arg {
@ -394,7 +394,7 @@ fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
type expected_tys = option<{inputs: ~[ty::arg],
output: ty::t}>;
fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>(
fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy owned>(
self: AC, rscope: RS,
proto: ast::proto,
decl: ast::fn_decl,

View file

@ -2154,7 +2154,7 @@ fn check_bounds_are_used(ccx: @crate_ctxt,
|_r| {},
|t| {
alt ty::get(t).struct {
ty::ty_param(idx, _) { tps_used[idx] = true; }
ty::ty_param({idx, _}) { tps_used[idx] = true; }
_ { }
}
true

View file

@ -87,8 +87,8 @@ class lookup {
loop {
// First, see whether this is an interface-bounded parameter.
alt ty::get(self.self_ty).struct {
ty::ty_param(n, did) {
self.add_candidates_from_param(n, did);
ty::ty_param(p) {
self.add_candidates_from_param(p.idx, p.def_id);
}
ty::ty_trait(did, substs) {
self.add_candidates_from_trait(did, substs);

View file

@ -20,23 +20,59 @@ this point a bit better.
import util::ppaux;
import syntax::print::pprust;
import infer::{resolve_type, resolve_all, force_all,
resolve_rvar, force_rvar};
resolve_rvar, force_rvar, fres};
import middle::kind::check_owned;
type rcx = @{fcx: @fn_ctxt, mut errors_reported: uint};
type rvt = visit::vt<rcx>;
enum rcx { rcx_({fcx: @fn_ctxt, mut errors_reported: uint}) }
type rvt = visit::vt<@rcx>;
impl methods for @rcx {
/// Try to resolve the type for the given node.
///
/// Note one important point: we do not attempt to resolve *region
/// variables* here. This is because regionck is essentially adding
/// constraints to those region variables and so may yet influence
/// how they are resolved.
///
/// Consider this silly example:
///
/// fn borrow(x: &int) -> &int {x}
/// fn foo(x: @int) -> int { /* block: B */
/// let b = borrow(x); /* region: <R0> */
/// *b
/// }
///
/// Here, the region of `b` will be `<R0>`. `<R0>` is constrainted
/// to be some subregion of the block B and some superregion of
/// the call. If we forced it now, we'd choose the smaller region
/// (the call). But that would make the *b illegal. Since we don't
/// resolve, the type of b will be `&<R0>.int` and then `*b` will require
/// that `<R0>` be bigger than the let and the `*b` expression, so we
/// will effectively resolve `<R0>` to be the block B.
fn resolve_type(unresolved_ty: ty::t) -> fres<ty::t> {
resolve_type(self.fcx.infcx, unresolved_ty,
(resolve_all | force_all) -
(resolve_rvar | force_rvar))
}
/// Try to resolve the type for the given node.
fn resolve_node_type(id: ast::node_id) -> fres<ty::t> {
self.resolve_type(self.fcx.node_ty(id))
}
}
fn regionck_expr(fcx: @fn_ctxt, e: @ast::expr) {
let rcx = @{fcx:fcx, mut errors_reported: 0u};
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
let v = regionck_visitor();
v.visit_expr(e, rcx, v);
v.visit_expr(e, @rcx, v);
}
fn regionck_fn(fcx: @fn_ctxt,
_decl: ast::fn_decl,
blk: ast::blk) {
let rcx = @{fcx:fcx, mut errors_reported: 0u};
let rcx = rcx_({fcx:fcx, mut errors_reported: 0u});
let v = regionck_visitor();
v.visit_block(blk, rcx, v);
v.visit_block(blk, @rcx, v);
}
fn regionck_visitor() -> rvt {
@ -49,11 +85,11 @@ fn regionck_visitor() -> rvt {
with *visit::default_visitor()})
}
fn visit_item(_item: @ast::item, &&_rcx: rcx, _v: rvt) {
fn visit_item(_item: @ast::item, &&_rcx: @rcx, _v: rvt) {
// Ignore items
}
fn visit_local(l: @ast::local, &&rcx: rcx, v: rvt) {
fn visit_local(l: @ast::local, &&rcx: @rcx, v: rvt) {
let e = rcx.errors_reported;
v.visit_pat(l.node.pat, rcx, v);
if e != rcx.errors_reported {
@ -66,7 +102,7 @@ fn visit_local(l: @ast::local, &&rcx: rcx, v: rvt) {
}
}
fn visit_pat(p: @ast::pat, &&rcx: rcx, v: rvt) {
fn visit_pat(p: @ast::pat, &&rcx: @rcx, v: rvt) {
let fcx = rcx.fcx;
alt p.node {
ast::pat_ident(path, _)
@ -80,11 +116,11 @@ fn visit_pat(p: @ast::pat, &&rcx: rcx, v: rvt) {
visit::visit_pat(p, rcx, v);
}
fn visit_block(b: ast::blk, &&rcx: rcx, v: rvt) {
fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) {
visit::visit_block(b, rcx, v);
}
fn visit_expr(e: @ast::expr, &&rcx: rcx, v: rvt) {
fn visit_expr(e: @ast::expr, &&rcx: @rcx, v: rvt) {
#debug["visit_expr(e=%s)", pprust::expr_to_str(e)];
alt e.node {
@ -99,6 +135,41 @@ fn visit_expr(e: @ast::expr, &&rcx: rcx, v: rvt) {
_ { }
}
}
ast::expr_cast(source, _) {
// Determine if we are casting `source` to an iface instance.
// If so, we have to be sure that the type of the source obeys
// the iface's region bound.
//
// Note: there is a subtle point here concerning type
// parameters. It is possible that the type of `source`
// contains type parameters, which in turn may contain regions
// that are not visible to us (only the caller knows about
// them). The kind checker is ultimately responsible for
// guaranteeing region safety in that particular case. There
// is an extensive comment on the function
// check_cast_for_escaping_regions() in kind.rs explaining how
// it goes about doing that.
alt rcx.resolve_node_type(e.id) {
result::err(_) => { ret; /* typeck will fail anyhow */ }
result::ok(target_ty) => {
alt ty::get(target_ty).struct {
ty::ty_trait(_, substs) {
let iface_region = alt substs.self_r {
some(r) => {r}
none => {ty::re_static}
};
let source_ty = rcx.fcx.expr_ty(source);
constrain_regions_in_type(rcx, iface_region,
e.span, source_ty);
}
_ { }
}
}
};
}
_ { }
}
@ -106,7 +177,7 @@ fn visit_expr(e: @ast::expr, &&rcx: rcx, v: rvt) {
visit::visit_expr(e, rcx, v);
}
fn visit_stmt(s: @ast::stmt, &&rcx: rcx, v: rvt) {
fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) {
visit::visit_stmt(s, rcx, v);
}
@ -114,37 +185,13 @@ fn visit_stmt(s: @ast::stmt, &&rcx: rcx, v: rvt) {
// references a region that is not in scope for that node. Returns
// false if an error is reported; this is used to cause us to cut off
// region checking for that subtree to avoid reporting tons of errors.
fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
let fcx = rcx.fcx;
// Try to resolve the type. If we encounter an error, then typeck
// is going to fail anyway, so just stop here and let typeck
// report errors later on in the writeback phase.
//
// Note one important point: we do not attempt to resolve *region
// variables* here. This is because regionck is essentially adding
// constraints to those region variables and so may yet influence
// how they are resolved.
//
// Consider this silly example:
//
// fn borrow(x: &int) -> &int {x}
// fn foo(x: @int) -> int { /* block: B */
// let b = borrow(x); /* region: <R0> */
// *b
// }
//
// Here, the region of `b` will be `<R0>`. `<R0>` is constrainted
// to be some subregion of the block B and some superregion of
// the call. If we forced it now, we'd choose the smaller region
// (the call). But that would make the *b illegal. Since we don't
// resolve, the type of b will be `&<R0>.int` and then `*b` will require
// that `<R0>` be bigger than the let and the `*b` expression, so we
// will effectively resolve `<R0>` to be the block B.
let ty0 = fcx.node_ty(id);
let ty = alt resolve_type(fcx.infcx, ty0,
(resolve_all | force_all) -
(resolve_rvar | force_rvar)) {
let ty = alt rcx.resolve_node_type(id) {
result::err(_) { ret true; }
result::ok(ty) { ty }
};
@ -153,21 +200,29 @@ fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
let tcx = fcx.ccx.tcx;
let encl_region = ty::encl_region(tcx, id);
#debug["visit_node(ty=%s, id=%d, encl_region=%s, ty0=%s)",
#debug["visit_node(ty=%s, id=%d, encl_region=%s)",
ppaux::ty_to_str(tcx, ty),
id,
ppaux::region_to_str(tcx, encl_region),
ppaux::ty_to_str(tcx, ty0)];
ppaux::region_to_str(tcx, encl_region)];
// Otherwise, look at the type and see if it is a region pointer.
ret constrain_regions_in_type(rcx, encl_region, span, ty);
}
fn constrain_regions_in_type(
rcx: @rcx,
encl_region: ty::region,
span: span,
ty: ty::t) -> bool {
let e = rcx.errors_reported;
ty::walk_regions_and_ty(
tcx, ty,
rcx.fcx.ccx.tcx, ty,
|r| constrain_region(rcx, encl_region, span, r),
|t| ty::type_has_regions(t));
ret (e == rcx.errors_reported);
fn constrain_region(rcx: rcx,
fn constrain_region(rcx: @rcx,
encl_region: ty::region,
span: span,
region: ty::region) {

View file

@ -64,7 +64,7 @@ fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
};
let ty = fixup_ty(fcx, sp, ty);
alt ty::get(ty).struct {
ty::ty_param(n, did) {
ty::ty_param({idx: n, def_id: did}) {
let mut n_bound = 0u;
for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
alt bound {

View file

@ -69,7 +69,9 @@ fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
}
impl methods for @crate_ctxt {
fn to_ty<RS: region_scope copy>(rs: RS, ast_ty: @ast::ty) -> ty::t {
fn to_ty<RS: region_scope copy owned>(
rs: RS, ast_ty: @ast::ty) -> ty::t {
ast_ty_to_ty(self, rs, ast_ty)
}
}

View file

@ -195,7 +195,7 @@ export resolve_type, resolve_region;
export resolve_borrowings;
export methods; // for infer_ctxt
export unify_methods; // for infer_ctxt
export fixup_err, fixup_err_to_str;
export fres, fixup_err, fixup_err_to_str;
export assignment;
export root, to_str;
export int_ty_set_all;
@ -1789,7 +1789,7 @@ fn super_tys<C:combine>(
}
}
(ty::ty_param(a_n, _), ty::ty_param(b_n, _)) if a_n == b_n {
(ty::ty_param(a_p), ty::ty_param(b_p)) if a_p.idx == b_p.idx {
ok(a)
}

View file

@ -38,7 +38,7 @@ impl of region_scope for type_rscope {
}
enum anon_rscope = {anon: ty::region, base: region_scope};
fn in_anon_rscope<RS: region_scope copy>(self: RS, r: ty::region)
fn in_anon_rscope<RS: region_scope copy owned>(self: RS, r: ty::region)
-> @anon_rscope {
@anon_rscope({anon: r, base: self as region_scope})
}
@ -52,7 +52,8 @@ impl of region_scope for @anon_rscope {
}
enum binding_rscope = {base: region_scope};
fn in_binding_rscope<RS: region_scope copy>(self: RS) -> @binding_rscope {
fn in_binding_rscope<RS: region_scope copy owned>(self: RS)
-> @binding_rscope {
let base = self as region_scope;
@binding_rscope({base: base})
}

View file

@ -216,7 +216,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
}
ty_var(v) { v.to_str() }
ty_var_integral(v) { v.to_str() }
ty_param(id, _) {
ty_param({idx: id, _}) {
~"'" + str::from_bytes(~[('a' as u8) + (id as u8)])
}
ty_self { ~"self" }

View file

@ -13,7 +13,7 @@ fn item_check(t: &tree) -> int {
}
}
fn bottom_up_tree(arena: &a.arena::arena, item: int, depth: int) -> &a.tree {
fn bottom_up_tree(arena: &arena::arena, item: int, depth: int) -> &tree {
if depth > 0 {
ret new(*arena) node(bottom_up_tree(arena, 2 * item - 1, depth - 1),
bottom_up_tree(arena, 2 * item, depth - 1),

View file

@ -3,7 +3,7 @@ fn to_lambda1(f: fn@(uint) -> uint) -> fn@(uint) -> uint {
}
fn to_lambda2(b: fn(uint) -> uint) -> fn@(uint) -> uint {
ret to_lambda1({|x| b(x)}); //~ ERROR not an owned value
ret to_lambda1({|x| b(x)}); //~ ERROR value may contain borrowed pointers
}
fn main() {

View file

@ -0,0 +1,20 @@
iface repeat<A> { fn get() -> A; }
impl<A:copy> of repeat<A> for @A {
fn get() -> A { *self }
}
fn repeater<A:copy>(v: @A) -> repeat<A> {
// Note: owned kind is not necessary as A appears in the iface type
v as repeat::<A> // No
}
fn main() {
// Here, an error results as the type of y is inferred to
// repeater<&lt/3> where lt is the block.
let y = { //~ ERROR reference is not valid outside of its lifetime
let x = &3;
repeater(@x)
};
assert 3 == *(y.get()); //~ ERROR reference is not valid outside of its lifetime
}

View file

@ -0,0 +1,35 @@
// A dummy iface/impl that work close over any type. The iface will
// be parameterized by a region due to the &self/int constraint.
iface foo {
fn foo(i: &self/int) -> int;
}
impl<T:copy> of foo for T {
fn foo(i: &self/int) -> int {*i}
}
fn to_foo<T:copy>(t: T) {
// This version is ok because, although T may contain borrowed
// pointers, it never escapes the fn body. We know this because
// the type of foo includes a region which will be resolved to
// the fn body itself.
let v = &3;
let x = {f:t} as foo;
assert x.foo(v) == 3;
}
fn to_foo_2<T:copy>(t: T) -> foo {
// Not OK---T may contain borrowed ptrs and it is going to escape
// as part of the returned foo value
{f:t} as foo //~ ERROR value may contain borrowed pointers; use `owned` bound
}
fn to_foo_3<T:copy owned>(t: T) -> foo {
// OK---T may escape as part of the returned foo value, but it is
// owned and hence does not contain borrowed ptrs
{f:t} as foo
}
fn main() {
}

View file

@ -0,0 +1,11 @@
iface foo { fn foo(); }
fn to_foo<T: copy foo>(t: T) -> foo {
t as foo //~ ERROR value may contain borrowed pointers; use `owned` bound
}
fn to_foo2<T: copy foo owned>(t: T) -> foo {
t as foo
}
fn main() {}

View file

@ -1,5 +1,5 @@
fn copy1<T: copy>(t: T) -> fn@() -> T {
fn@() -> T { t } //~ ERROR not an owned value
fn@() -> T { t } //~ ERROR value may contain borrowed pointers
}
fn copy2<T: copy owned>(t: T) -> fn@() -> T {
@ -8,12 +8,12 @@ fn copy2<T: copy owned>(t: T) -> fn@() -> T {
fn main() {
let x = &3;
copy2(&x); //~ ERROR instantiating a type parameter with an incompatible type
copy2(&x); //~ ERROR missing `owned`
copy2(@3);
copy2(@&x); //~ ERROR instantiating a type parameter with an incompatible type
copy2(@&x); //~ ERROR missing `owned`
copy2(fn@() {});
copy2(fn~() {}); //~ WARNING instantiating copy type parameter with a not implicitly copyable type
copy2(fn&() {}); //~ ERROR instantiating a type parameter with an incompatible type
copy2(fn&() {}); //~ ERROR missing `copy owned`
}

View file

@ -0,0 +1,10 @@
fn sum_slice(x: &[int]) -> int {
let mut sum = 0;
for x.each |i| { sum += i; }
ret sum;
}
fn main() {
let x = @[1, 2, 3];
assert sum_slice(x) == 6;
}

View file

@ -1,7 +1,7 @@
iface hax { }
impl <A> of hax for A { }
fn perform_hax<T>(x: @T) -> hax {
fn perform_hax<T: owned>(x: @T) -> hax {
x as hax
}

View file

@ -1,7 +1,7 @@
iface hax { }
impl <A> of hax for A { }
fn perform_hax<T>(x: @T) -> hax {
fn perform_hax<T: owned>(x: @T) -> hax {
x as hax
}

View file

@ -0,0 +1,16 @@
iface repeat<A> { fn get() -> A; }
impl<A:copy> of repeat<A> for @A {
fn get() -> A { *self }
}
fn repeater<A:copy>(v: @A) -> repeat<A> {
// Note: owned kind is not necessary as A appears in the iface type
v as repeat::<A> // No
}
fn main() {
let x = &3;
let y = repeater(@x);
assert *x == *(y.get());
}

View file

@ -230,9 +230,9 @@ impl ptr_visitor<V: ty_visitor movable_ptr>
}
fn visit_rptr(mtbl: uint, inner: *tydesc) -> bool {
self.align_to::<&static.u8>();
self.align_to::<&static/u8>();
if ! self.inner.visit_rptr(mtbl, inner) { ret false; }
self.bump_past::<&static.u8>();
self.bump_past::<&static/u8>();
true
}
@ -268,9 +268,9 @@ impl ptr_visitor<V: ty_visitor movable_ptr>
}
fn visit_evec_slice(mtbl: uint, inner: *tydesc) -> bool {
self.align_to::<&static.[u8]>();
self.align_to::<&static/[u8]>();
if ! self.inner.visit_evec_slice(mtbl, inner) { ret false; }
self.bump_past::<&static.[u8]>();
self.bump_past::<&static/[u8]>();
true
}
@ -427,9 +427,9 @@ impl ptr_visitor<V: ty_visitor movable_ptr>
}
fn visit_self() -> bool {
self.align_to::<&static.u8>();
self.align_to::<&static/u8>();
if ! self.inner.visit_self() { ret false; }
self.align_to::<&static.u8>();
self.align_to::<&static/u8>();
true
}

View file

@ -1,4 +1,4 @@
fn f(x : &a.int) -> &a.int {
fn f(x : &a/int) -> &a/int {
ret &*x;
}

View file

@ -1,6 +1,6 @@
// A very limited test of the "bottom" region
fn produce_static<T>() -> &static.T { fail; }
fn produce_static<T>() -> &static/T { fail; }
fn foo<T>(x: &T) -> &uint { produce_static() }

View file

@ -8,14 +8,14 @@ fn ok(f: fn@(x: &uint)) {
// f's type should be a subtype of g's type), because f can be
// used in any context that expects g's type. But this currently
// fails.
let mut g: fn@(y: &r.uint) = fn@(x: &r.uint) { };
let mut g: fn@(y: &r/uint) = fn@(x: &r/uint) { };
g = f;
}
// This version is the same as above, except that here, g's type is
// inferred.
fn ok_inferred(f: fn@(x: &uint)) {
let mut g = fn@(x: &r.uint) { };
let mut g = fn@(x: &r/uint) { };
g = f;
}

View file

@ -19,5 +19,8 @@ fn get_v(gc: get_ctxt) -> uint {
fn main() {
let ctxt = { v: 22u };
let hc = { c: &ctxt };
assert get_v(hc as get_ctxt) == 22u;
// This no longer works, interestingly, due to the ownership
// requirement. Perhaps this ownership requirement is too strict.
// assert get_v(hc as get_ctxt) == 22u;
}

View file

@ -15,12 +15,12 @@ type ccx = {
x: int
};
fn alloc(_bcx : &a.arena) -> &a.bcx unsafe {
fn alloc(_bcx : &arena) -> &bcx unsafe {
ret unsafe::reinterpret_cast(
libc::malloc(sys::size_of::<bcx/&blk>() as libc::size_t));
}
fn h(bcx : &a.bcx) -> &a.bcx {
fn h(bcx : &bcx) -> &bcx {
ret alloc(bcx.fcx.arena);
}

View file

@ -1,4 +1,4 @@
fn region_identity(x: &r.uint) -> &r.uint { x }
fn region_identity(x: &uint) -> &uint { x }
fn apply<T>(t: T, f: fn(T) -> T) -> T { f(t) }