prevent regions from escaping in ifaces; remove &r.T syntax
This commit is contained in:
parent
eb0a34c398
commit
e0ea67a2a6
36 changed files with 411 additions and 130 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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('|');
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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") }
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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() {
|
||||
|
|
20
src/test/compile-fail/kindck-owned-trait-contains.rs
Normal file
20
src/test/compile-fail/kindck-owned-trait-contains.rs
Normal 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<</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
|
||||
}
|
35
src/test/compile-fail/kindck-owned-trait-scoped.rs
Normal file
35
src/test/compile-fail/kindck-owned-trait-scoped.rs
Normal 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() {
|
||||
}
|
11
src/test/compile-fail/kindck-owned-trait.rs
Normal file
11
src/test/compile-fail/kindck-owned-trait.rs
Normal 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() {}
|
|
@ -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`
|
||||
}
|
||||
|
|
10
src/test/run-pass/borrowck-borrow-from-at-vec.rs
Normal file
10
src/test/run-pass/borrowck-borrow-from-at-vec.rs
Normal 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;
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
16
src/test/run-pass/kindck-owned-trait-contains-1.rs
Normal file
16
src/test/run-pass/kindck-owned-trait-contains-1.rs
Normal 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());
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
fn f(x : &a.int) -> &a.int {
|
||||
fn f(x : &a/int) -> &a/int {
|
||||
ret &*x;
|
||||
}
|
||||
|
||||
|
|
|
@ -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() }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue