Skip to content

Commit d4722e5

Browse files
committed
Trade stack closure copyability for type soundness.
1 parent 98c169e commit d4722e5

File tree

3 files changed

+37
-15
lines changed

3 files changed

+37
-15
lines changed

src/librustc/middle/kind.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ fn with_appropriate_checker(cx: Context, id: node_id,
171171
// check that only immutable variables are implicitly copied in
172172
check_imm_free_var(cx, fv.def, fv.span);
173173

174-
check_freevar_bounds(cx, fv.span, var_t, bounds);
174+
check_freevar_bounds(cx, fv.span, var_t, bounds, None);
175175
}
176176

177177
fn check_for_box(cx: Context, fv: &freevar_entry, bounds: ty::BuiltinBounds) {
@@ -182,13 +182,18 @@ fn with_appropriate_checker(cx: Context, id: node_id,
182182
// check that only immutable variables are implicitly copied in
183183
check_imm_free_var(cx, fv.def, fv.span);
184184

185-
check_freevar_bounds(cx, fv.span, var_t, bounds);
185+
check_freevar_bounds(cx, fv.span, var_t, bounds, None);
186186
}
187187

188-
fn check_for_block(cx: Context, fv: &freevar_entry, bounds: ty::BuiltinBounds) {
188+
fn check_for_block(cx: Context, fv: &freevar_entry,
189+
bounds: ty::BuiltinBounds, region: ty::Region) {
189190
let id = ast_util::def_id_of_def(fv.def).node;
190191
let var_t = ty::node_id_to_type(cx.tcx, id);
191-
check_freevar_bounds(cx, fv.span, var_t, bounds);
192+
// FIXME(#3569): Figure out whether the implicit borrow is actually
193+
// mutable. Currently we assume all upvars are referenced mutably.
194+
let implicit_borrowed_type = ty::mk_mut_rptr(cx.tcx, region, var_t);
195+
check_freevar_bounds(cx, fv.span, implicit_borrowed_type,
196+
bounds, Some(var_t));
192197
}
193198

194199
fn check_for_bare(cx: Context, fv: @freevar_entry) {
@@ -205,8 +210,9 @@ fn with_appropriate_checker(cx: Context, id: node_id,
205210
ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, bounds: bounds, _}) => {
206211
b(|cx, fv| check_for_box(cx, fv, bounds))
207212
}
208-
ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds, _}) => {
209-
b(|cx, fv| check_for_block(cx, fv, bounds))
213+
ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds,
214+
region: region, _}) => {
215+
b(|cx, fv| check_for_block(cx, fv, bounds, region))
210216
}
211217
ty::ty_bare_fn(_) => {
212218
b(check_for_bare)
@@ -366,14 +372,21 @@ pub fn check_typaram_bounds(cx: Context,
366372
}
367373

368374
pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t,
369-
bounds: ty::BuiltinBounds)
375+
bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
370376
{
371377
do check_builtin_bounds(cx, ty, bounds) |missing| {
372-
cx.tcx.sess.span_err(
373-
sp,
374-
fmt!("cannot capture variable of type `%s`, which does not fulfill \
375-
`%s`, in a bounded closure",
376-
ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx)));
378+
// Will be Some if the freevar is implicitly borrowed (stack closure).
379+
// Emit a less mysterious error message in this case.
380+
match referenced_ty {
381+
Some(rty) => cx.tcx.sess.span_err(sp,
382+
fmt!("cannot implicitly borrow variable of type `%s` in a bounded \
383+
stack closure (implicit reference does not fulfill `%s`)",
384+
ty_to_str(cx.tcx, rty), missing.user_string(cx.tcx))),
385+
None => cx.tcx.sess.span_err(sp,
386+
fmt!("cannot capture variable of type `%s`, which does \
387+
not fulfill `%s`, in a bounded closure",
388+
ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx))),
389+
}
377390
cx.tcx.sess.span_note(
378391
sp,
379392
fmt!("this closure's environment must satisfy `%s`",

src/librustc/middle/ty.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,9 @@ impl TypeContents {
18281828
// Currently all noncopyable existentials are 2nd-class types
18291829
// behind owned pointers. With dynamically-sized types, remove
18301830
// this assertion.
1831-
assert!(self.intersects(TC_OWNED_POINTER));
1831+
assert!(self.intersects(TC_OWNED_POINTER) ||
1832+
// (...or stack closures without a copy bound.)
1833+
self.intersects(TC_BORROWED_POINTER));
18321834
}
18331835
let tc = TC_MANAGED + TC_DTOR + TypeContents::sendable(cx);
18341836
self.intersects(tc)
@@ -2194,7 +2196,14 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
21942196
ast::Once => TC_ONCE_CLOSURE,
21952197
ast::Many => TC_NONE
21962198
};
2197-
st + rt + ot
2199+
// Prevent noncopyable types captured in the environment from being copied.
2200+
let ct = if cty.bounds.contains_elem(BoundCopy) ||
2201+
cty.sigil == ast::ManagedSigil {
2202+
TC_NONE
2203+
} else {
2204+
TC_NONCOPY_TRAIT
2205+
};
2206+
st + rt + ot + ct
21982207
}
21992208

22002209
fn trait_contents(store: TraitStore, mutbl: ast::mutability,

src/test/compile-fail/kindck-owned.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ fn main() {
2929
copy2(boxed);
3030
let owned: ~fn() = || {};
3131
copy2(owned); //~ ERROR does not fulfill `Copy`
32-
let borrowed: &fn() = || {};
32+
let borrowed: &fn:Copy() = || {};
3333
copy2(borrowed); //~ ERROR does not fulfill `'static`
3434
}

0 commit comments

Comments
 (0)