From 7f0916cced7bdfb7e68af91aaee068585bb935a8 Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Thu, 13 Jun 2013 19:37:18 -0400 Subject: [PATCH 1/2] Check closure freevar kinds against destination environment bounds (#3569) --- src/librustc/middle/kind.rs | 77 +++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index a4cac540a7e78..035bb9b0c58b1 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -82,8 +82,6 @@ pub fn check_crate(tcx: ty::ctxt, tcx.sess.abort_if_errors(); } -type check_fn = @fn(Context, @freevar_entry); - fn check_struct_safe_for_destructor(cx: Context, span: span, struct_did: def_id) { @@ -163,30 +161,43 @@ fn check_item(item: @item, (cx, visitor): (Context, visit::vt)) { // Yields the appropriate function to check the kind of closed over // variables. `id` is the node_id for some expression that creates the // closure. -fn with_appropriate_checker(cx: Context, id: node_id, b: &fn(check_fn)) { - fn check_for_uniq(cx: Context, fv: @freevar_entry) { +fn with_appropriate_checker(cx: Context, id: node_id, + b: &fn(checker: &fn(Context, @freevar_entry))) { + fn check_for_uniq(cx: Context, fv: @freevar_entry, bounds: ty::BuiltinBounds) { // all captured data must be owned, regardless of whether it is // moved in or copied in. let id = ast_util::def_id_of_def(fv.def).node; let var_t = ty::node_id_to_type(cx.tcx, id); + + // FIXME(#3569): Once closure capabilities are restricted based on their + // incoming bounds, make this check conditional based on the bounds. if !check_owned(cx, var_t, fv.span) { return; } // check that only immutable variables are implicitly copied in check_imm_free_var(cx, fv.def, fv.span); + + check_freevar_bounds(cx, fv.span, var_t, bounds); } - fn check_for_box(cx: Context, fv: @freevar_entry) { + fn check_for_box(cx: Context, fv: @freevar_entry, bounds: ty::BuiltinBounds) { // all captured data must be owned let id = ast_util::def_id_of_def(fv.def).node; let var_t = ty::node_id_to_type(cx.tcx, id); + + // FIXME(#3569): Once closure capabilities are restricted based on their + // incoming bounds, make this check conditional based on the bounds. if !check_durable(cx.tcx, var_t, fv.span) { return; } // check that only immutable variables are implicitly copied in check_imm_free_var(cx, fv.def, fv.span); + + check_freevar_bounds(cx, fv.span, var_t, bounds); } - fn check_for_block(_cx: Context, _fv: @freevar_entry) { - // no restrictions + fn check_for_block(cx: Context, fv: @freevar_entry, bounds: ty::BuiltinBounds) { + let id = ast_util::def_id_of_def(fv.def).node; + let var_t = ty::node_id_to_type(cx.tcx, id); + check_freevar_bounds(cx, fv.span, var_t, bounds); } 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)) { let fty = ty::node_id_to_type(cx.tcx, id); match ty::get(fty).sty { - ty::ty_closure(ty::ClosureTy {sigil: OwnedSigil, _}) => { - b(check_for_uniq) + ty::ty_closure(ty::ClosureTy {sigil: OwnedSigil, bounds: bounds, _}) => { + b(|cx, fv| check_for_uniq(cx, fv, bounds)) } - ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, _}) => { - b(check_for_box) + ty::ty_closure(ty::ClosureTy {sigil: ManagedSigil, bounds: bounds, _}) => { + b(|cx, fv| check_for_box(cx, fv, bounds)) } - ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, _}) => { - b(check_for_block) + ty::ty_closure(ty::ClosureTy {sigil: BorrowedSigil, bounds: bounds, _}) => { + b(|cx, fv| check_for_block(cx, fv, bounds)) } ty::ty_bare_fn(_) => { b(check_for_bare) @@ -272,7 +283,7 @@ pub fn check_expr(e: @expr, (cx, v): (Context, visit::vt)) { type_param_defs.repr(cx.tcx)); } for ts.iter().zip(type_param_defs.iter()).advance |(&ty, type_param_def)| { - check_bounds(cx, type_parameter_id, e.span, ty, type_param_def) + check_typaram_bounds(cx, type_parameter_id, e.span, ty, type_param_def) } } } @@ -315,7 +326,7 @@ fn check_ty(aty: @Ty, (cx, v): (Context, visit::vt)) { let type_param_defs = ty::lookup_item_type(cx.tcx, did).generics.type_param_defs; for ts.iter().zip(type_param_defs.iter()).advance |(&ty, type_param_def)| { - check_bounds(cx, aty.id, aty.span, ty, type_param_def) + check_typaram_bounds(cx, aty.id, aty.span, ty, type_param_def) } } } @@ -324,19 +335,26 @@ fn check_ty(aty: @Ty, (cx, v): (Context, visit::vt)) { visit::visit_ty(aty, (cx, v)); } -pub fn check_bounds(cx: Context, - _type_parameter_id: node_id, - sp: span, - ty: ty::t, - type_param_def: &ty::TypeParameterDef) +pub fn check_builtin_bounds(cx: Context, ty: ty::t, bounds: ty::BuiltinBounds) + -> ty::BuiltinBounds // returns the missing bounds { let kind = ty::type_contents(cx.tcx, ty); let mut missing = ty::EmptyBuiltinBounds(); - for type_param_def.bounds.builtin_bounds.each |bound| { + for bounds.each |bound| { if !kind.meets_bound(cx.tcx, bound) { missing.add(bound); } } + missing +} + +pub fn check_typaram_bounds(cx: Context, + _type_parameter_id: node_id, + sp: span, + ty: ty::t, + type_param_def: &ty::TypeParameterDef) +{ + let missing = check_builtin_bounds(cx, ty, type_param_def.bounds.builtin_bounds); if !missing.is_empty() { cx.tcx.sess.span_err( sp, @@ -347,6 +365,23 @@ pub fn check_bounds(cx: Context, } } +pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t, + bounds: ty::BuiltinBounds) +{ + let missing = check_builtin_bounds(cx, ty, bounds); + if !missing.is_empty() { + cx.tcx.sess.span_err( + sp, + fmt!("cannot capture variable of type `%s`, which does not fulfill \ + `%s`, in a bounded closure", + ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx))); + cx.tcx.sess.span_note( + sp, + fmt!("this closure's environment must satisfy `%s`", + bounds.user_string(cx.tcx))); + } +} + fn is_nullary_variant(cx: Context, ex: @expr) -> bool { match ex.node { expr_path(_) => { From efc3b1bad0d4397fe0e9b74fb5d2840c7e794605 Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Thu, 13 Jun 2013 19:38:36 -0400 Subject: [PATCH 2/2] Add basic test cases for closure bounds. (#3569) --- ...closure-bounds-cant-promote-superkind-1.rs | 19 +++++++++++++++ ...closure-bounds-cant-promote-superkind-2.rs | 19 +++++++++++++++ ...bounds-cant-promote-superkind-in-struct.rs | 20 ++++++++++++++++ ...re-bounds-copy-cant-capture-noncopyable.rs | 24 +++++++++++++++++++ ...ure-bounds-static-cant-capture-borrowed.rs | 22 +++++++++++++++++ .../closure-bounds-can-capture-chan.rs | 23 ++++++++++++++++++ .../closure-bounds-promote-subkind-1.rs | 19 +++++++++++++++ .../closure-bounds-promote-subkind-2.rs | 19 +++++++++++++++ ...losure-bounds-promote-subkind-in-struct.rs | 20 ++++++++++++++++ 9 files changed, 185 insertions(+) create mode 100644 src/test/compile-fail/closure-bounds-cant-promote-superkind-1.rs create mode 100644 src/test/compile-fail/closure-bounds-cant-promote-superkind-2.rs create mode 100644 src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs create mode 100644 src/test/compile-fail/closure-bounds-copy-cant-capture-noncopyable.rs create mode 100644 src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs create mode 100644 src/test/run-pass/closure-bounds-can-capture-chan.rs create mode 100644 src/test/run-pass/closure-bounds-promote-subkind-1.rs create mode 100644 src/test/run-pass/closure-bounds-promote-subkind-2.rs create mode 100644 src/test/run-pass/closure-bounds-promote-subkind-in-struct.rs diff --git a/src/test/compile-fail/closure-bounds-cant-promote-superkind-1.rs b/src/test/compile-fail/closure-bounds-cant-promote-superkind-1.rs new file mode 100644 index 0000000000000..38dd9186718dc --- /dev/null +++ b/src/test/compile-fail/closure-bounds-cant-promote-superkind-1.rs @@ -0,0 +1,19 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn bar(_blk: &fn:Copy()) { +} + +fn foo(blk: &fn()) { + bar(blk); //~ ERROR expected bounds `Copy` but found no bounds +} + +fn main() { +} diff --git a/src/test/compile-fail/closure-bounds-cant-promote-superkind-2.rs b/src/test/compile-fail/closure-bounds-cant-promote-superkind-2.rs new file mode 100644 index 0000000000000..2239aad780354 --- /dev/null +++ b/src/test/compile-fail/closure-bounds-cant-promote-superkind-2.rs @@ -0,0 +1,19 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn bar(_blk: &fn:Const+Owned()) { +} + +fn foo(blk: &fn:Copy+Owned()) { + bar(blk); //~ ERROR expected bounds `Owned+Const` but found bounds `Copy+Owned` +} + +fn main() { +} diff --git a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs new file mode 100644 index 0000000000000..c3c8467233c4d --- /dev/null +++ b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs @@ -0,0 +1,20 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct X { + field: @fn:Copy(), +} + +fn foo(blk: @fn()) -> X { + return X { field: blk }; //~ ERROR expected bounds `Copy` but found no bounds +} + +fn main() { +} diff --git a/src/test/compile-fail/closure-bounds-copy-cant-capture-noncopyable.rs b/src/test/compile-fail/closure-bounds-copy-cant-capture-noncopyable.rs new file mode 100644 index 0000000000000..e5be6fb136611 --- /dev/null +++ b/src/test/compile-fail/closure-bounds-copy-cant-capture-noncopyable.rs @@ -0,0 +1,24 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::comm; + +fn foo(blk: ~fn:Copy()) { + blk(); +} + +fn main() { + let (p,c) = comm::stream(); + do foo { // shouldn't be legal + c.send(()); //~ ERROR cannot capture variable of type `std::comm::Chan<()>`, which does not fulfill `Copy`, in a bounded closure + //~^ NOTE this closure's environment must satisfy `Copy` + } + p.recv(); +} diff --git a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs new file mode 100644 index 0000000000000..3096a386512eb --- /dev/null +++ b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs @@ -0,0 +1,22 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn bar(blk: &fn:'static()) { +} + +fn foo(x: &()) { + do bar { + let _ = x; //~ ERROR cannot capture variable of type `&()`, which does not fulfill `'static`, in a bounded closure + //~^ NOTE this closure's environment must satisfy `'static` + } +} + +fn main() { +} diff --git a/src/test/run-pass/closure-bounds-can-capture-chan.rs b/src/test/run-pass/closure-bounds-can-capture-chan.rs new file mode 100644 index 0000000000000..dbf9a513a606a --- /dev/null +++ b/src/test/run-pass/closure-bounds-can-capture-chan.rs @@ -0,0 +1,23 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::comm; + +fn foo(blk: ~fn:Owned()) { + blk(); +} + +fn main() { + let (p,c) = comm::stream(); + do foo { // shouldn't be legal + c.send(()); + } + p.recv(); +} diff --git a/src/test/run-pass/closure-bounds-promote-subkind-1.rs b/src/test/run-pass/closure-bounds-promote-subkind-1.rs new file mode 100644 index 0000000000000..d387c61fcdcb3 --- /dev/null +++ b/src/test/run-pass/closure-bounds-promote-subkind-1.rs @@ -0,0 +1,19 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn bar(_blk: &fn()) { +} + +fn foo(blk: &fn:Copy()) { + bar(blk); +} + +fn main() { +} diff --git a/src/test/run-pass/closure-bounds-promote-subkind-2.rs b/src/test/run-pass/closure-bounds-promote-subkind-2.rs new file mode 100644 index 0000000000000..80f525a3d2ab4 --- /dev/null +++ b/src/test/run-pass/closure-bounds-promote-subkind-2.rs @@ -0,0 +1,19 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn bar(_blk: &fn:Const+Owned()) { +} + +fn foo(blk: &fn:Const+Copy+Owned()) { + bar(blk); +} + +fn main() { +} diff --git a/src/test/run-pass/closure-bounds-promote-subkind-in-struct.rs b/src/test/run-pass/closure-bounds-promote-subkind-in-struct.rs new file mode 100644 index 0000000000000..208694b838df2 --- /dev/null +++ b/src/test/run-pass/closure-bounds-promote-subkind-in-struct.rs @@ -0,0 +1,20 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct X { + field: @fn(), +} + +fn foo(blk: @fn:Copy()) -> X { + return X { field: blk }; +} + +fn main() { +}