From 459353e1078ad50c9088980104b4e940840a26ae Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 27 Sep 2011 18:45:35 -0700 Subject: [PATCH] Prevent copies of resources into various things --- src/comp/middle/kind.rs | 67 ++++++++++++++++++ src/fuzzer/fuzzer.rs | 2 +- src/test/compile-fail/copy-res-into-box.rs | 5 +- src/test/compile-fail/copy-res-into-rec.rs | 3 +- src/test/compile-fail/copy-res-into-tag.rs | 2 +- src/test/compile-fail/copy-res-into-tup.rs | 3 +- src/test/compile-fail/copy-res-into-unique.rs | 15 ++++ src/test/run-pass/foreach-box-drop.rs | 2 +- src/test/run-pass/foreach-unique-drop.rs | 2 +- src/test/run-pass/generic-box.rs | 2 +- src/test/run-pass/generic-exterior-box.rs | 2 +- src/test/run-pass/init-res-into-things.rs | 70 +++++++++++++++++++ 12 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 src/test/compile-fail/copy-res-into-unique.rs create mode 100644 src/test/run-pass/init-res-into-things.rs diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs index 2e117723f03e..c5e8991f804d 100644 --- a/src/comp/middle/kind.rs +++ b/src/comp/middle/kind.rs @@ -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"); + } + } _ { } } } diff --git a/src/fuzzer/fuzzer.rs b/src/fuzzer/fuzzer.rs index 635145a08d8d..acd77e647538 100644 --- a/src/fuzzer/fuzzer.rs +++ b/src/fuzzer/fuzzer.rs @@ -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( +fn check_variants_T<@T>( crate: ast::crate, codemap: codemap::codemap, filename: str, diff --git a/src/test/compile-fail/copy-res-into-box.rs b/src/test/compile-fail/copy-res-into-box.rs index 84f59b743326..15f22b5eb6e3 100644 --- a/src/test/compile-fail/copy-res-into-box.rs +++ b/src/test/compile-fail/copy-res-into-box.rs @@ -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; diff --git a/src/test/compile-fail/copy-res-into-rec.rs b/src/test/compile-fail/copy-res-into-rec.rs index 92e5132f849e..f22c4fd25ab9 100644 --- a/src/test/compile-fail/copy-res-into-rec.rs +++ b/src/test/compile-fail/copy-res-into-rec.rs @@ -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; } diff --git a/src/test/compile-fail/copy-res-into-tag.rs b/src/test/compile-fail/copy-res-into-tag.rs index 4ee957377319..358277c4785e 100644 --- a/src/test/compile-fail/copy-res-into-tag.rs +++ b/src/test/compile-fail/copy-res-into-tag.rs @@ -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; } diff --git a/src/test/compile-fail/copy-res-into-tup.rs b/src/test/compile-fail/copy-res-into-tup.rs index cb5391a18814..4808b947045e 100644 --- a/src/test/compile-fail/copy-res-into-tup.rs +++ b/src/test/compile-fail/copy-res-into-tup.rs @@ -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; } diff --git a/src/test/compile-fail/copy-res-into-unique.rs b/src/test/compile-fail/copy-res-into-unique.rs new file mode 100644 index 000000000000..380f1e44a006 --- /dev/null +++ b/src/test/compile-fail/copy-res-into-unique.rs @@ -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; +} \ No newline at end of file diff --git a/src/test/run-pass/foreach-box-drop.rs b/src/test/run-pass/foreach-box-drop.rs index 960ef4e20a62..1e892cf6711c 100644 --- a/src/test/run-pass/foreach-box-drop.rs +++ b/src/test/run-pass/foreach-box-drop.rs @@ -1,6 +1,6 @@ -obj ob(k: K) { +obj ob<@K>(k: K) { iter foo() -> @{a: K} { put @{a: k}; } } diff --git a/src/test/run-pass/foreach-unique-drop.rs b/src/test/run-pass/foreach-unique-drop.rs index fb2f901a7d79..3b3bfe937c75 100644 --- a/src/test/run-pass/foreach-unique-drop.rs +++ b/src/test/run-pass/foreach-unique-drop.rs @@ -1,5 +1,5 @@ -obj ob(k: K) { +obj ob<@K>(k: K) { iter foo() -> ~{a: K} { put ~{a: k}; } } diff --git a/src/test/run-pass/generic-box.rs b/src/test/run-pass/generic-box.rs index 3e37834f47b1..4f132a79a812 100644 --- a/src/test/run-pass/generic-box.rs +++ b/src/test/run-pass/generic-box.rs @@ -1,6 +1,6 @@ -fn box(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::({x: 1, y: 2, z: 3}); diff --git a/src/test/run-pass/generic-exterior-box.rs b/src/test/run-pass/generic-exterior-box.rs index 7573bff74e85..c1ce4257b14c 100644 --- a/src/test/run-pass/generic-exterior-box.rs +++ b/src/test/run-pass/generic-exterior-box.rs @@ -2,7 +2,7 @@ type recbox = {x: @T}; -fn reclift(t: T) -> recbox { ret {x: @t}; } +fn reclift<@T>(t: T) -> recbox { ret {x: @t}; } fn main() { let foo: int = 17; diff --git a/src/test/run-pass/init-res-into-things.rs b/src/test/run-pass/init-res-into-things.rs new file mode 100644 index 000000000000..fc1868ec07e1 --- /dev/null +++ b/src/test/run-pass/init-res-into-things.rs @@ -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(); +}