Skip to content

Commit bd573be

Browse files
committed
change region scope of call arguments, old one was unsound
improve error message to describe kind of deref'd ptr using sigil
1 parent c9eb9ee commit bd573be

8 files changed

+81
-50
lines changed

src/rustc/middle/region.rs

+35-45
Original file line numberDiff line numberDiff line change
@@ -162,43 +162,38 @@ type ctxt = {
162162
def_map: resolve::def_map,
163163
region_map: region_map,
164164

165-
// These two fields (parent and closure_parent) specify the parent
166-
// scope of the current expression. The parent scope is the
167-
// innermost block, call, or alt expression during the execution
168-
// of which the current expression will be evaluated. Generally
169-
// speaking, the innermost parent scope is also the closest
170-
// suitable ancestor in the AST tree.
165+
// The parent scope is the innermost block, call, or alt
166+
// expression during the execution of which the current expression
167+
// will be evaluated. Generally speaking, the innermost parent
168+
// scope is also the closest suitable ancestor in the AST tree.
171169
//
172-
// However, there are two subtle cases where the parent scope for
173-
// an expression is not strictly derived from the AST. The first
174-
// such exception concerns call arguments and the second concerns
175-
// closures (which, at least today, are always call arguments).
176-
// Consider:
170+
// There is a subtle point concerning call arguments. Imagine
171+
// you have a call:
177172
//
178173
// { // block a
179-
// foo( // call b
174+
// foo( // call b
180175
// x,
181-
// y,
182-
// fn&() {
183-
// // fn body c
184-
// })
176+
// y);
185177
// }
186178
//
187-
// Here, the parent of the three argument expressions is
188-
// actually the block `a`, not the call `b`, because they will
189-
// be evaluated before the call conceptually takes place.
190-
// However, the body of the closure is parented by the call
191-
// `b` (it cannot be invoked except during that call, after
192-
// all).
179+
// In what lifetime are the expressions `x` and `y` evaluated? At
180+
// first, I imagine the answer was the block `a`, as the arguments
181+
// are evaluated before the call takes place. But this turns out
182+
// to be wrong. The lifetime of the call must encompass the
183+
// argument evaluation as well.
193184
//
194-
// To capture these patterns, we use two fields. The first,
195-
// parent, is the parent scope of a normal expression. The
196-
// second, closure_parent, is the parent scope that a closure body
197-
// ought to use. These only differ in the case of calls, where
198-
// the closure parent is the call, but the parent is the container
199-
// of the call.
200-
parent: parent,
201-
closure_parent: parent
185+
// The reason is that evaluation of an earlier argument could
186+
// create a borrow which exists during the evaluation of later
187+
// arguments. Consider this torture test, for example,
188+
//
189+
// fn test1(x: @mut ~int) {
190+
// foo(&**x, *x = ~5);
191+
// }
192+
//
193+
// Here, the first argument `&**x` will be a borrow of the `~int`,
194+
// but the second argument overwrites that very value! Bad.
195+
// (This test is borrowck-pure-scope-in-call.rs, btw)
196+
parent: parent
202197
};
203198

204199
// Returns true if `subscope` is equal to or is lexically nested inside
@@ -291,8 +286,7 @@ fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
291286
record_parent(cx, blk.node.id);
292287

293288
// Descend.
294-
let new_cx: ctxt = {parent: some(blk.node.id),
295-
closure_parent: some(blk.node.id) with cx};
289+
let new_cx: ctxt = {parent: some(blk.node.id) with cx};
296290
visit::visit_block(blk, new_cx, visitor);
297291
}
298292

@@ -325,14 +319,12 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
325319
alt expr.node {
326320
ast::expr_call(*) {
327321
#debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
328-
let new_cx = {closure_parent: some(expr.id) with cx};
322+
let new_cx = {parent: some(expr.id) with cx};
329323
visit::visit_expr(expr, new_cx, visitor);
330324
}
331325
ast::expr_alt(subexpr, _, _) {
332326
#debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
333-
let new_cx = {parent: some(expr.id),
334-
closure_parent: some(expr.id)
335-
with cx};
327+
let new_cx = {parent: some(expr.id) with cx};
336328
visit::visit_expr(expr, new_cx, visitor);
337329
}
338330
ast::expr_fn(_, _, _, cap_clause) |
@@ -358,7 +350,7 @@ fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
358350

359351
fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
360352
// Items create a new outer block scope as far as we're concerned.
361-
let new_cx: ctxt = {closure_parent: none, parent: none with cx};
353+
let new_cx: ctxt = {parent: none with cx};
362354
visit::visit_item(item, new_cx, visitor);
363355
}
364356

@@ -370,19 +362,18 @@ fn resolve_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
370362
visit::fk_item_fn(*) | visit::fk_method(*) | visit::fk_res(*) |
371363
visit::fk_ctor(*) | visit::fk_dtor(*) {
372364
// Top-level functions are a root scope.
373-
{parent: some(id), closure_parent: some(id) with cx}
365+
{parent: some(id) with cx}
374366
}
375367

376368
visit::fk_anon(*) | visit::fk_fn_block(*) {
377-
// Closures use the closure_parent.
378-
{parent: cx.closure_parent with cx}
369+
// Closures continue with the inherited scope.
370+
cx
379371
}
380372
};
381373

382374
#debug["visiting fn with body %d. cx.parent: %? \
383-
cx.closure_parent: %? fn_cx.parent: %?",
384-
body.node.id, cx.parent,
385-
cx.closure_parent, fn_cx.parent];
375+
fn_cx.parent: %?",
376+
body.node.id, cx.parent, fn_cx.parent];
386377

387378
for decl.inputs.each { |input|
388379
cx.region_map.insert(input.id, body.node.id);
@@ -396,8 +387,7 @@ fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
396387
let cx: ctxt = {sess: sess,
397388
def_map: def_map,
398389
region_map: map::int_hash(),
399-
parent: none,
400-
closure_parent: none};
390+
parent: none};
401391
let visitor = visit::mk_vt(@{
402392
visit_block: resolve_block,
403393
visit_item: resolve_item,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// xfail-fast (compile-flags unsupported on windows)
2+
// compile-flags:--borrowck=err
3+
4+
pure fn pure_borrow(_x: &int, _y: ()) {}
5+
6+
fn test1(x: @mut ~int) {
7+
// Here, evaluating the second argument actually invalidates the
8+
// first borrow, even though it occurs outside of the scope of the
9+
// borrow!
10+
pure_borrow(*x, *x = ~5); //! ERROR illegal borrow unless pure: unique value in aliasable, mutable location
11+
//!^ NOTE impure due to assigning to dereference of mutable @ pointer
12+
}
13+
14+
fn test2() {
15+
let mut x = ~1;
16+
17+
// Same, but for loanable data:
18+
19+
pure_borrow(x, x = ~5); //! ERROR assigning to mutable local variable prohibited due to outstanding loan
20+
//!^ NOTE loan of mutable local variable granted here
21+
}
22+
23+
fn main() {
24+
}

src/test/compile-fail/fn-variance-3.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ fn main() {
2020
// mutability check will fail, because the
2121
// type of r has been inferred to be
2222
// fn(@const int) -> @const int
23-
*r(@mut 3) = 4; //! ERROR assigning to dereference of const pointer
23+
*r(@mut 3) = 4; //! ERROR assigning to dereference of const @ pointer
2424
}

src/test/compile-fail/mutable-huh-box-assign.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
fn main() {
22
fn f(&&v: @const int) {
3-
*v = 1 //! ERROR assigning to dereference of const pointer
3+
*v = 1 //! ERROR assigning to dereference of const @ pointer
44
}
55

66
let v = @0;

src/test/compile-fail/mutable-huh-ptr-assign.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std;
22

33
fn main() {
44
unsafe fn f(&&v: *const int) {
5-
*v = 1 //! ERROR assigning to dereference of const pointer
5+
*v = 1 //! ERROR assigning to dereference of const * pointer
66
}
77

88
unsafe {

src/test/compile-fail/mutable-huh-unique-assign.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
fn main() {
22
fn f(&&v: ~const int) {
3-
*v = 1 //! ERROR assigning to dereference of const pointer
3+
*v = 1 //! ERROR assigning to dereference of const ~ pointer
44
}
55

66
let v = ~0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn borrow(x: &int, f: fn(x: &int)) {
2+
f(x)
3+
}
4+
5+
fn test1(x: @~int) {
6+
// Right now, at least, this induces a copy of the unique pointer:
7+
borrow({*x}) { |p|
8+
let x_a = ptr::addr_of(**x);
9+
assert (x_a as uint) != (p as uint);
10+
assert unsafe{*x_a} == *p;
11+
}
12+
}
13+
14+
fn main() {
15+
test1(@~22);
16+
}

src/test/run-pass/regions-addr-of-ret.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ fn f(x : &a.int) -> &a.int {
33
}
44

55
fn main() {
6-
log(error, #fmt("%d", *f(&3)));
6+
let three = &3;
7+
log(error, #fmt("%d", *f(three)));
78
}
89

0 commit comments

Comments
 (0)