Skip to content

Commit 2c21b58

Browse files
committed
Check closure freevar kinds against destination environment bounds (rust-lang#3569)
1 parent 1120f8c commit 2c21b58

File tree

1 file changed

+56
-21
lines changed

1 file changed

+56
-21
lines changed

src/librustc/middle/kind.rs

+56-21
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ pub fn check_crate(tcx: ty::ctxt,
8181
tcx.sess.abort_if_errors();
8282
}
8383

84-
type check_fn = @fn(Context, @freevar_entry);
85-
8684
fn check_struct_safe_for_destructor(cx: Context,
8785
span: span,
8886
struct_did: def_id) {
@@ -162,30 +160,43 @@ fn check_item(item: @item, (cx, visitor): (Context, visit::vt<Context>)) {
162160
// Yields the appropriate function to check the kind of closed over
163161
// variables. `id` is the node_id for some expression that creates the
164162
// closure.
165-
fn with_appropriate_checker(cx: Context, id: node_id, b: &fn(check_fn)) {
166-
fn check_for_uniq(cx: Context, fv: @freevar_entry) {
163+
fn with_appropriate_checker(cx: Context, id: node_id,
164+
b: &fn(checker: &fn(Context, @freevar_entry))) {
165+
fn check_for_uniq(cx: Context, fv: @freevar_entry, bounds: ty::BuiltinBounds) {
167166
// all captured data must be owned, regardless of whether it is
168167
// moved in or copied in.
169168
let id = ast_util::def_id_of_def(fv.def).node;
170169
let var_t = ty::node_id_to_type(cx.tcx, id);
170+
171+
// FIXME(#3569): Once closure capabilities are restricted based on their
172+
// incoming bounds, make this check conditional based on the bounds.
171173
if !check_owned(cx, var_t, fv.span) { return; }
172174

173175
// check that only immutable variables are implicitly copied in
174176
check_imm_free_var(cx, fv.def, fv.span);
177+
178+
check_freevar_bounds(cx, fv.span, var_t, bounds);
175179
}
176180

177-
fn check_for_box(cx: Context, fv: @freevar_entry) {
181+
fn check_for_box(cx: Context, fv: @freevar_entry, bounds: ty::BuiltinBounds) {
178182
// all captured data must be owned
179183
let id = ast_util::def_id_of_def(fv.def).node;
180184
let var_t = ty::node_id_to_type(cx.tcx, id);
185+
186+
// FIXME(#3569): Once closure capabilities are restricted based on their
187+
// incoming bounds, make this check conditional based on the bounds.
181188
if !check_durable(cx.tcx, var_t, fv.span) { return; }
182189

183190
// check that only immutable variables are implicitly copied in
184191
check_imm_free_var(cx, fv.def, fv.span);
192+
193+
check_freevar_bounds(cx, fv.span, var_t, bounds);
185194
}
186195

187-
fn check_for_block(_cx: Context, _fv: @freevar_entry) {
188-
// no restrictions
196+
fn check_for_block(cx: Context, fv: @freevar_entry, bounds: ty::BuiltinBounds) {
197+
let id = ast_util::def_id_of_def(fv.def).node;
198+
let var_t = ty::node_id_to_type(cx.tcx, id);
199+
check_freevar_bounds(cx, fv.span, var_t, bounds);
189200
}
190201

191202
fn check_for_bare(cx: Context, fv: @freevar_entry) {
@@ -196,14 +207,14 @@ fn with_appropriate_checker(cx: Context, id: node_id, b: &fn(check_fn)) {
196207

197208
let fty = ty::node_id_to_type(cx.tcx, id);
198209
match ty::get(fty).sty {
199-
ty::ty_closure(ty::ClosureTy {sigil: OwnedSigil, _}) => {
200-
b(check_for_uniq)
210+
ty::ty_closure(ty::ClosureTy {sigil: OwnedSigil, bounds: bounds, _}) => {
211+
b(|cx, fv| check_for_uniq(cx, fv, bounds))
201212
}
202-
ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, _}) => {
203-
b(check_for_box)
213+
ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, bounds: bounds, _}) => {
214+
b(|cx, fv| check_for_box(cx, fv, bounds))
204215
}
205-
ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, _}) => {
206-
b(check_for_block)
216+
ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds, _}) => {
217+
b(|cx, fv| check_for_block(cx, fv, bounds))
207218
}
208219
ty::ty_bare_fn(_) => {
209220
b(check_for_bare)
@@ -271,7 +282,7 @@ pub fn check_expr(e: @expr, (cx, v): (Context, visit::vt<Context>)) {
271282
type_param_defs.repr(cx.tcx));
272283
}
273284
for ts.iter().zip(type_param_defs.iter()).advance |(&ty, type_param_def)| {
274-
check_bounds(cx, type_parameter_id, e.span, ty, type_param_def)
285+
check_typaram_bounds(cx, type_parameter_id, e.span, ty, type_param_def)
275286
}
276287
}
277288
}
@@ -314,7 +325,7 @@ fn check_ty(aty: @Ty, (cx, v): (Context, visit::vt<Context>)) {
314325
let type_param_defs =
315326
ty::lookup_item_type(cx.tcx, did).generics.type_param_defs;
316327
for ts.iter().zip(type_param_defs.iter()).advance |(&ty, type_param_def)| {
317-
check_bounds(cx, aty.id, aty.span, ty, type_param_def)
328+
check_typaram_bounds(cx, aty.id, aty.span, ty, type_param_def)
318329
}
319330
}
320331
}
@@ -323,19 +334,26 @@ fn check_ty(aty: @Ty, (cx, v): (Context, visit::vt<Context>)) {
323334
visit::visit_ty(aty, (cx, v));
324335
}
325336

326-
pub fn check_bounds(cx: Context,
327-
_type_parameter_id: node_id,
328-
sp: span,
329-
ty: ty::t,
330-
type_param_def: &ty::TypeParameterDef)
337+
pub fn check_builtin_bounds(cx: Context, ty: ty::t, bounds: ty::BuiltinBounds)
338+
-> ty::BuiltinBounds // returns the missing bounds
331339
{
332340
let kind = ty::type_contents(cx.tcx, ty);
333341
let mut missing = ty::EmptyBuiltinBounds();
334-
for type_param_def.bounds.builtin_bounds.each |bound| {
342+
for bounds.each |bound| {
335343
if !kind.meets_bound(cx.tcx, bound) {
336344
missing.add(bound);
337345
}
338346
}
347+
missing
348+
}
349+
350+
pub fn check_typaram_bounds(cx: Context,
351+
_type_parameter_id: node_id,
352+
sp: span,
353+
ty: ty::t,
354+
type_param_def: &ty::TypeParameterDef)
355+
{
356+
let missing = check_builtin_bounds(cx, ty, type_param_def.bounds.builtin_bounds);
339357
if !missing.is_empty() {
340358
cx.tcx.sess.span_err(
341359
sp,
@@ -346,6 +364,23 @@ pub fn check_bounds(cx: Context,
346364
}
347365
}
348366

367+
pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t,
368+
bounds: ty::BuiltinBounds)
369+
{
370+
let missing = check_builtin_bounds(cx, ty, bounds);
371+
if !missing.is_empty() {
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)));
377+
cx.tcx.sess.span_note(
378+
sp,
379+
fmt!("this closure's environment must satisfy `%s`",
380+
bounds.user_string(cx.tcx)));
381+
}
382+
}
383+
349384
fn is_nullary_variant(cx: Context, ex: @expr) -> bool {
350385
match ex.node {
351386
expr_path(_) => {

0 commit comments

Comments
 (0)