Skip to content

Commit c9bd9df

Browse files
committed
avoid capture of bound regions when infering types for closure
expressions. cc #2981
1 parent 7400881 commit c9bd9df

14 files changed

+189
-37
lines changed

src/rustc/metadata/tyencode.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ fn enc_bound_region(w: io::writer, br: ty::bound_region) {
160160
w.write_str(*s);
161161
w.write_char(']')
162162
}
163+
ty::br_cap_avoid(id, br) {
164+
w.write_char('c');
165+
w.write_int(id);
166+
w.write_char('|');
167+
enc_bound_region(w, *br);
168+
}
163169
}
164170
}
165171

src/rustc/middle/ty.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export ty_self, mk_self, type_has_self;
100100
export ty_class;
101101
export region, bound_region, encl_region;
102102
export re_bound, re_free, re_scope, re_static, re_var;
103-
export br_self, br_anon, br_named;
103+
export br_self, br_anon, br_named, br_cap_avoid;
104104
export get, type_has_params, type_needs_infer, type_has_regions;
105105
export type_has_resources, type_id;
106106
export tbox_has_flag;
@@ -351,9 +351,24 @@ enum region {
351351
}
352352

353353
enum bound_region {
354-
br_self, // The self region for classes, impls
355-
br_anon, // The anonymous region parameter for a given function.
356-
br_named(ast::ident) // A named region parameter.
354+
/// The self region for classes, impls (&T in a type defn or &self/T)
355+
br_self,
356+
357+
/// Anonymous region parameter for a given fn (&T)
358+
br_anon,
359+
360+
/// Named region parameters for functions (a in &a/T)
361+
br_named(ast::ident),
362+
363+
/// Handles capture-avoiding substitution in a rather subtle case. If you
364+
/// have a closure whose argument types are being inferred based on the
365+
/// expected type, and the expected type includes bound regions, then we
366+
/// will wrap those bound regions in a br_cap_avoid() with the id of the
367+
/// fn expression. This ensures that the names are not "captured" by the
368+
/// enclosing scope, which may define the same names. For an example of
369+
/// where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs
370+
/// and regions-ret-borrowed-1.rs.
371+
br_cap_avoid(ast::node_id, @bound_region),
357372
}
358373

359374
type opt_region = option<region>;
@@ -1665,7 +1680,7 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
16651680
ret result;
16661681
}
16671682

1668-
// True if instantiating an instance of `ty` requires an instance of `r_ty`.
1683+
// True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
16691684
fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
16701685

16711686
fn type_requires(cx: ctxt, seen: @mut ~[def_id],
@@ -2001,6 +2016,7 @@ fn hash_bound_region(br: bound_region) -> uint {
20012016
ty::br_self { 0u }
20022017
ty::br_anon { 1u }
20032018
ty::br_named(str) { str::hash(*str) }
2019+
ty::br_cap_avoid(id, br) { id as uint | hash_bound_region(*br) }
20042020
}
20052021
}
20062022

src/rustc/middle/typeck/check.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,11 +1084,21 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
10841084
expected: option<ty::t>) {
10851085
let tcx = fcx.ccx.tcx;
10861086

1087+
// Find the expected input/output types (if any). Careful to
1088+
// avoid capture of bound regions in the expected type. See
1089+
// def'n of br_cap_avoid() for a more lengthy explanation of
1090+
// what's going on here.
10871091
let expected_tys = do unpack_expected(fcx, expected) |sty| {
10881092
alt sty {
1089-
ty::ty_fn(fn_ty) {some({inputs:fn_ty.inputs,
1090-
output:fn_ty.output})}
1091-
_ {none}
1093+
ty::ty_fn(fn_ty) => {
1094+
let {fn_ty, _} =
1095+
replace_bound_regions_in_fn_ty(
1096+
tcx, @nil, none, fn_ty,
1097+
|br| ty::re_bound(ty::br_cap_avoid(expr.id, @br)));
1098+
some({inputs:fn_ty.inputs,
1099+
output:fn_ty.output})
1100+
}
1101+
_ => {none}
10921102
}
10931103
};
10941104

@@ -1984,15 +1994,26 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
19841994
writeback::resolve_type_vars_in_expr(fcx, e);
19851995
}
19861996

1997+
/// Checks whether a type can be created without an instance of itself.
1998+
/// This is similar but different from the question of whether a type
1999+
/// can be represented. For example, the following type:
2000+
///
2001+
/// enum foo { none, some(foo) }
2002+
///
2003+
/// is instantiable but is not representable. Similarly, the type
2004+
///
2005+
/// enum foo { some(@foo) }
2006+
///
2007+
/// is representable, but not instantiable.
19872008
fn check_instantiable(tcx: ty::ctxt,
19882009
sp: span,
19892010
item_id: ast::node_id) {
1990-
let rty = ty::node_id_to_type(tcx, item_id);
1991-
if !ty::is_instantiable(tcx, rty) {
2011+
let item_ty = ty::node_id_to_type(tcx, item_id);
2012+
if !ty::is_instantiable(tcx, item_ty) {
19922013
tcx.sess.span_err(sp, #fmt["this type cannot be instantiated \
19932014
without an instance of itself; \
19942015
consider using `option<%s>`",
1995-
ty_to_str(tcx, rty)]);
2016+
ty_to_str(tcx, item_ty)]);
19962017
}
19972018
}
19982019

@@ -2063,6 +2084,9 @@ fn check_enum_variants(ccx: @crate_ctxt,
20632084
}
20642085

20652086
// Check that it is possible to instantiate this enum:
2087+
//
2088+
// This *sounds* like the same that as representable, but it's
2089+
// not. See def'n of `check_instantiable()` for details.
20662090
check_instantiable(ccx.tcx, sp, id);
20672091
}
20682092

src/rustc/util/ppaux.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import std::map::hashmap;
22
import middle::ty;
3-
import middle::ty::{arg, bound_region, br_anon, br_named, canon_mode};
3+
import middle::ty::{arg, canon_mode};
4+
import middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid};
45
import middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method};
56
import middle::ty::{mt, re_bound, re_free, re_scope, re_var, region, t};
67
import middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum};
@@ -20,10 +21,20 @@ import driver::session::session;
2021

2122
fn bound_region_to_str(cx: ctxt, br: bound_region) -> ~str {
2223
alt br {
23-
br_anon { ~"&" }
24-
br_named(str) { #fmt["&%s", *str] }
25-
br_self if cx.sess.ppregions() { ~"&<self>" }
26-
br_self { ~"&self" }
24+
br_anon => { ~"&" }
25+
br_named(str) => { #fmt["&%s", *str] }
26+
br_self if cx.sess.ppregions() => { ~"&<self>" }
27+
br_self => { ~"&self" }
28+
29+
// FIXME(#3011) -- even if this arm is removed, exhaustiveness checking
30+
// does not fail
31+
br_cap_avoid(id, br) => {
32+
if cx.sess.ppregions() {
33+
#fmt["br_cap_avoid(%?, %s)", id, bound_region_to_str(cx, *br)]
34+
} else {
35+
bound_region_to_str(cx, *br)
36+
}
37+
}
2738
}
2839
}
2940

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// xfail-test #2978
2+
3+
fn call(x: @{mut f: fn~()}) {
4+
x.f(); //~ ERROR foo
5+
//~^ NOTE bar
6+
}
7+
8+
fn main() {}

src/test/compile-fail/borrowck-lend-args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fn borrow_from_arg_imm_ref(&&v: ~int) {
55
}
66

77
fn borrow_from_arg_mut_ref(&v: ~int) {
8-
borrow(v); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
8+
borrow(v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
99
//~^ NOTE impure due to access to impure function
1010
}
1111

src/test/compile-fail/borrowck-mut-deref-comp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
enum foo = ~int;
22

33
fn borrow(x: @mut foo) {
4-
let y = &***x; //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
4+
let _y = &***x; //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
55
*x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
66
}
77

src/test/compile-fail/borrowck-pure-scope-in-call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ fn test1(x: @mut ~int) {
44
// Here, evaluating the second argument actually invalidates the
55
// first borrow, even though it occurs outside of the scope of the
66
// borrow!
7-
pure_borrow(*x, *x = ~5); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
7+
pure_borrow(*x, *x = ~5); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
88
//~^ NOTE impure due to assigning to dereference of mutable @ pointer
99
}
1010

src/test/compile-fail/borrowck-uniq-via-box.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
fn borrow(_v: &int) {}
22

33
fn box_mut(v: @mut ~int) {
4-
borrow(*v); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
4+
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
55
//~^ NOTE impure due to access to impure function
66
}
77

88
fn box_rec_mut(v: @{mut f: ~int}) {
9-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
9+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
1010
//~^ NOTE impure due to access to impure function
1111
}
1212

1313
fn box_mut_rec(v: @mut {f: ~int}) {
14-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
14+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
1515
//~^ NOTE impure due to access to impure function
1616
}
1717

1818
fn box_mut_recs(v: @mut {f: {g: {h: ~int}}}) {
19-
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
19+
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
2020
//~^ NOTE impure due to access to impure function
2121
}
2222

@@ -33,27 +33,27 @@ fn box_imm_recs(v: @{f: {g: {h: ~int}}}) {
3333
}
3434

3535
fn box_const(v: @const ~int) {
36-
borrow(*v); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
36+
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
3737
//~^ NOTE impure due to access to impure function
3838
}
3939

4040
fn box_rec_const(v: @{const f: ~int}) {
41-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
41+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
4242
//~^ NOTE impure due to access to impure function
4343
}
4444

4545
fn box_recs_const(v: @{f: {g: {const h: ~int}}}) {
46-
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
46+
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
4747
//~^ NOTE impure due to access to impure function
4848
}
4949

5050
fn box_const_rec(v: @const {f: ~int}) {
51-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
51+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
5252
//~^ NOTE impure due to access to impure function
5353
}
5454

5555
fn box_const_recs(v: @const {f: {g: {h: ~int}}}) {
56-
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
56+
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
5757
//~^ NOTE impure due to access to impure function
5858
}
5959

src/test/compile-fail/borrowck-uniq-via-ref.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
fn borrow(_v: &int) {}
22

33
fn box_mut(v: &mut ~int) {
4-
borrow(*v); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
4+
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
55
//~^ NOTE impure due to access to impure function
66
}
77

88
fn box_rec_mut(v: &{mut f: ~int}) {
9-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
9+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
1010
//~^ NOTE impure due to access to impure function
1111
}
1212

1313
fn box_mut_rec(v: &mut {f: ~int}) {
14-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
14+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
1515
//~^ NOTE impure due to access to impure function
1616
}
1717

1818
fn box_mut_recs(v: &mut {f: {g: {h: ~int}}}) {
19-
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
19+
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
2020
//~^ NOTE impure due to access to impure function
2121
}
2222

@@ -33,27 +33,27 @@ fn box_imm_recs(v: &{f: {g: {h: ~int}}}) {
3333
}
3434

3535
fn box_const(v: &const ~int) {
36-
borrow(*v); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
36+
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
3737
//~^ NOTE impure due to access to impure function
3838
}
3939

4040
fn box_rec_const(v: &{const f: ~int}) {
41-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
41+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
4242
//~^ NOTE impure due to access to impure function
4343
}
4444

4545
fn box_recs_const(v: &{f: {g: {const h: ~int}}}) {
46-
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
46+
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
4747
//~^ NOTE impure due to access to impure function
4848
}
4949

5050
fn box_const_rec(v: &const {f: ~int}) {
51-
borrow(v.f); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
51+
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
5252
//~^ NOTE impure due to access to impure function
5353
}
5454

5555
fn box_const_recs(v: &const {f: {g: {h: ~int}}}) {
56-
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: unique value in aliasable, mutable location
56+
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
5757
//~^ NOTE impure due to access to impure function
5858
}
5959

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
iface deref {
2+
fn get() -> int;
3+
}
4+
5+
impl of deref for &int {
6+
fn get() -> int {
7+
*self
8+
}
9+
}
10+
11+
fn with<R: deref>(f: fn(x: &int) -> R) -> int {
12+
f(&3).get()
13+
}
14+
15+
fn return_it() -> int {
16+
with(|o| o)
17+
//~^ ERROR reference is not valid outside of its lifetime, &
18+
//~^^ ERROR reference is not valid outside of its lifetime, &
19+
}
20+
21+
fn main() {
22+
let x = return_it();
23+
#debug["foo=%d", x];
24+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Similar to regions-ret-borrowed.rs, but using a named lifetime. At
2+
// some point regions-ret-borrowed reported an error but this file did
3+
// not, due to special hardcoding around the anonymous region.
4+
5+
fn with<R>(f: fn(x: &a/int) -> R) -> R {
6+
f(&3)
7+
}
8+
9+
fn return_it() -> &a/int {
10+
with(|o| o) //~ ERROR mismatched types
11+
//~^ ERROR reference is not valid outside of its lifetime
12+
}
13+
14+
fn main() {
15+
let x = return_it();
16+
#debug["foo=%d", *x];
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Ensure that you cannot use generic types to return a region outside
2+
// of its bound. Here, in the `return_it()` fn, we call with() but
3+
// with R bound to &int from the return_it. Meanwhile, with()
4+
// provides a value that is only good within its own stack frame. This
5+
// used to successfully compile because we failed to account for the
6+
// fact that fn(x: &int) rebound the region &.
7+
8+
fn with<R>(f: fn(x: &int) -> R) -> R {
9+
f(&3)
10+
}
11+
12+
fn return_it() -> &int {
13+
with(|o| o) //~ ERROR mismatched types
14+
//~^ ERROR reference is not valid outside of its lifetime
15+
}
16+
17+
fn main() {
18+
let x = return_it();
19+
#debug["foo=%d", *x];
20+
}

0 commit comments

Comments
 (0)