Prevent copies of resources into various things

This commit is contained in:
Brian Anderson 2011-09-27 18:45:35 -07:00
parent 9ba86178ef
commit 459353e107
12 changed files with 162 additions and 13 deletions

View File

@ -142,6 +142,52 @@ fn need_shared_lhs_rhs(tcx: ty::ctxt, a: @ast::expr, b: @ast::expr, op: str) {
need_expr_kind(tcx, b, ast::kind_shared, op + " rhs");
}
/*
This ... is a hack (I find myself writing that too often *sadface*).
We need to be able to put pinned kinds into other types but such operations
are conceptually copies, and pinned kinds can't do that, e.g.
let a = my_resource(x);
let b = @a; // no-go
So this function attempts to make a loophole where resources can be put into
other types as long as it's done in a safe way, specifically like
let b = @my_resource(x);
*/
fn need_shared_or_pinned_ctor(tcx: ty::ctxt, a: @ast::expr, descr: str) {
let tk = type_and_kind(tcx, a);
if tk.kind == ast::kind_pinned && !pinned_ctor(a) {
let err =
#fmt["mismatched kinds for %s: cannot copy pinned type %s",
descr, util::ppaux::ty_to_str(tcx, tk.ty)];
tcx.sess.span_err(a.span, err);
let note =
#fmt["try constructing %s directly into %s",
util::ppaux::ty_to_str(tcx, tk.ty), descr];
tcx.sess.span_note(a.span, note);
} else if tk.kind != ast::kind_pinned {
need_expr_kind(tcx, a, ast::kind_shared, descr);
}
fn pinned_ctor(a: @ast::expr) -> bool {
// FIXME: Technically a lambda block is also a pinned ctor
alt a.node {
ast::expr_call(cexpr, _) {
// Assuming that if it's a call that it's safe to move in, mostly
// because I don't know offhand how to ensure that it's a call
// specifically to a resource constructor
true
}
ast::expr_rec(_, _) {
true
}
_ { false }
}
}
}
fn check_expr(tcx: ty::ctxt, e: @ast::expr) {
alt e.node {
@ -189,6 +235,27 @@ fn check_expr(tcx: ty::ctxt, e: @ast::expr) {
}
}
}
ast::expr_unary(op, a) {
alt op {
ast::box(_) {
need_shared_or_pinned_ctor(tcx, a, "'@' operand");
}
ast::uniq(_) {
need_shared_or_pinned_ctor(tcx, a, "'~' operand");
}
_ { /* fall through */ }
}
}
ast::expr_rec(fields, _) {
for field in fields {
need_shared_or_pinned_ctor(tcx, field.node.expr, "record field");
}
}
ast::expr_tup(exprs) {
for expr in exprs {
need_shared_or_pinned_ctor(tcx, expr, "tuple parameter");
}
}
_ { }
}
}

View File

@ -230,7 +230,7 @@ fn check_variants_of_ast(crate: ast::crate, codemap: codemap::codemap,
check_variants_T(crate, codemap, filename, "ty", stolen.tys, pprust::ty_to_str, replace_ty_in_crate, cx);
}
fn check_variants_T<T>(
fn check_variants_T<@T>(
crate: ast::crate,
codemap: codemap::codemap,
filename: str,

View File

@ -1,5 +1,4 @@
// xfail-test
// error-pattern:mismatched kinds
// error-pattern:mismatched kinds for '@' operand
resource r(i: @mutable int) {
*i = *i + 1;
}
@ -9,7 +8,7 @@ fn main() {
{
let j <- r(i);
// No no no no no
let k = @j;
let k <- @j;
}
log_err *i;
assert *i == 2;

View File

@ -1,5 +1,4 @@
// xfail-test
// error-pattern:mismatched kinds
// error-pattern:mismatched kinds for record field
resource r(i: @mutable int) {
*i = *i + 1;
}

View File

@ -1,5 +1,5 @@
// xfail-test
// error-pattern:mismatched kinds
// error-pattern:mismatched kinds for tag parameter
resource r(i: @mutable int) {
*i = *i + 1;
}

View File

@ -1,5 +1,4 @@
// xfail-test
// error-pattern:mismatched kinds
// error-pattern:mismatched kinds for tuple parameter
resource r(i: @mutable int) {
*i = *i + 1;
}

View File

@ -0,0 +1,15 @@
// error-pattern:mismatched kinds for '~' operand
resource r(i: @mutable int) {
*i = *i + 1;
}
fn main() {
let i = @mutable 0;
{
let j <- r(i);
// No no no no no
let k <- ~j;
}
log_err *i;
assert *i == 2;
}

View File

@ -1,6 +1,6 @@
obj ob<K>(k: K) {
obj ob<@K>(k: K) {
iter foo() -> @{a: K} { put @{a: k}; }
}

View File

@ -1,5 +1,5 @@
obj ob<K>(k: K) {
obj ob<@K>(k: K) {
iter foo() -> ~{a: K} { put ~{a: k}; }
}

View File

@ -1,6 +1,6 @@
fn box<T>(x: {x: T, y: T, z: T}) -> @{x: T, y: T, z: T} { ret @x; }
fn box<@T>(x: {x: T, y: T, z: T}) -> @{x: T, y: T, z: T} { ret @x; }
fn main() {
let x: @{x: int, y: int, z: int} = box::<int>({x: 1, y: 2, z: 3});

View File

@ -2,7 +2,7 @@
type recbox<T> = {x: @T};
fn reclift<T>(t: T) -> recbox<T> { ret {x: @t}; }
fn reclift<@T>(t: T) -> recbox<T> { ret {x: @t}; }
fn main() {
let foo: int = 17;

View File

@ -0,0 +1,70 @@
// Resources can't be copied into other types but still need to be able
// to find their way into things.
resource r(i: @mutable int) {
*i = *i + 1;
}
fn test_box() {
let i = @mutable 0;
{
let a <- @r(i);
}
assert *i == 1;
}
fn test_rec() {
let i = @mutable 0;
{
let a <- {x: r(i)};
}
assert *i == 1;
}
fn test_tag() {
tag t {
t0(r);
}
let i = @mutable 0;
{
let a <- t0(r(i));
}
assert *i == 1;
}
fn test_tup() {
let i = @mutable 0;
{
let a <- (r(i), 0);
}
assert *i == 1;
}
fn test_unique() {
let i = @mutable 0;
{
let a <- ~r(i);
}
assert *i == 1;
}
fn test_box_rec() {
let i = @mutable 0;
{
let a <- @{
x: r(i)
};
}
assert *i == 1;
}
fn main() {
test_box();
test_rec();
// FIXME: tag constructors don't optimize their arguments into moves
// test_tag();
test_tup();
test_unique();
test_box_rec();
}