Skip to content

Commit 7c0a040

Browse files
committed
Check closure freevar kinds against destination environment bounds (rust-lang#3569)
1 parent f4ccb2f commit 7c0a040

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

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

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

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

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

192203
fn check_for_bare(cx: Context, fv: @freevar_entry) {
@@ -197,14 +208,14 @@ fn with_appropriate_checker(cx: Context, id: node_id, b: &fn(check_fn)) {
197208

198209
let fty = ty::node_id_to_type(cx.tcx, id);
199210
match ty::get(fty).sty {
200-
ty::ty_closure(ty::ClosureTy {sigil: OwnedSigil, _}) => {
201-
b(check_for_uniq)
211+
ty::ty_closure(ty::ClosureTy {sigil: OwnedSigil, bounds: bounds, _}) => {
212+
b(|cx, fv| check_for_uniq(cx, fv, bounds))
202213
}
203-
ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, _}) => {
204-
b(check_for_box)
214+
ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, bounds: bounds, _}) => {
215+
b(|cx, fv| check_for_box(cx, fv, bounds))
205216
}
206-
ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, _}) => {
207-
b(check_for_block)
217+
ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds, _}) => {
218+
b(|cx, fv| check_for_block(cx, fv, bounds))
208219
}
209220
ty::ty_bare_fn(_) => {
210221
b(check_for_bare)
@@ -272,7 +283,7 @@ pub fn check_expr(e: @expr, (cx, v): (Context, visit::vt<Context>)) {
272283
type_param_defs.repr(cx.tcx));
273284
}
274285
for ts.iter().zip(type_param_defs.iter()).advance |(&ty, type_param_def)| {
275-
check_bounds(cx, type_parameter_id, e.span, ty, type_param_def)
286+
check_typaram_bounds(cx, type_parameter_id, e.span, ty, type_param_def)
276287
}
277288
}
278289
}
@@ -315,7 +326,7 @@ fn check_ty(aty: @Ty, (cx, v): (Context, visit::vt<Context>)) {
315326
let type_param_defs =
316327
ty::lookup_item_type(cx.tcx, did).generics.type_param_defs;
317328
for ts.iter().zip(type_param_defs.iter()).advance |(&ty, type_param_def)| {
318-
check_bounds(cx, aty.id, aty.span, ty, type_param_def)
329+
check_typaram_bounds(cx, aty.id, aty.span, ty, type_param_def)
319330
}
320331
}
321332
}
@@ -324,19 +335,26 @@ fn check_ty(aty: @Ty, (cx, v): (Context, visit::vt<Context>)) {
324335
visit::visit_ty(aty, (cx, v));
325336
}
326337

327-
pub fn check_bounds(cx: Context,
328-
_type_parameter_id: node_id,
329-
sp: span,
330-
ty: ty::t,
331-
type_param_def: &ty::TypeParameterDef)
338+
pub fn check_builtin_bounds(cx: Context, ty: ty::t, bounds: ty::BuiltinBounds)
339+
-> ty::BuiltinBounds // returns the missing bounds
332340
{
333341
let kind = ty::type_contents(cx.tcx, ty);
334342
let mut missing = ty::EmptyBuiltinBounds();
335-
for type_param_def.bounds.builtin_bounds.each |bound| {
343+
for bounds.each |bound| {
336344
if !kind.meets_bound(cx.tcx, bound) {
337345
missing.add(bound);
338346
}
339347
}
348+
missing
349+
}
350+
351+
pub fn check_typaram_bounds(cx: Context,
352+
_type_parameter_id: node_id,
353+
sp: span,
354+
ty: ty::t,
355+
type_param_def: &ty::TypeParameterDef)
356+
{
357+
let missing = check_builtin_bounds(cx, ty, type_param_def.bounds.builtin_bounds);
340358
if !missing.is_empty() {
341359
cx.tcx.sess.span_err(
342360
sp,
@@ -347,6 +365,23 @@ pub fn check_bounds(cx: Context,
347365
}
348366
}
349367

368+
pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t,
369+
bounds: ty::BuiltinBounds)
370+
{
371+
let missing = check_builtin_bounds(cx, ty, bounds);
372+
if !missing.is_empty() {
373+
cx.tcx.sess.span_err(
374+
sp,
375+
fmt!("cannot capture variable of type `%s`, which does not fulfill \
376+
`%s`, in a bounded closure",
377+
ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx)));
378+
cx.tcx.sess.span_note(
379+
sp,
380+
fmt!("this closure's environment must satisfy `%s`",
381+
bounds.user_string(cx.tcx)));
382+
}
383+
}
384+
350385
fn is_nullary_variant(cx: Context, ex: @expr) -> bool {
351386
match ex.node {
352387
expr_path(_) => {

0 commit comments

Comments
 (0)