Move checks for closure bounds out of kind.rs

This commit is contained in:
Niko Matsakis 2014-09-22 14:47:06 -04:00
parent 034f69ec4b
commit 2ec305d1bc
10 changed files with 74 additions and 171 deletions

View file

@ -146,10 +146,7 @@ register_diagnostics!(
E0139,
E0140,
E0141,
E0143,
E0144,
E0145,
E0146,
E0152,
E0153,
E0154,

View file

@ -30,11 +30,6 @@ pub struct Context<'a,'tcx:'a> {
}
impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
fn visit_fn(&mut self, fk: visit::FnKind, fd: &'v FnDecl,
b: &'v Block, s: Span, n: NodeId) {
check_fn(self, fk, fd, b, s, n);
}
fn visit_ty(&mut self, t: &Ty) {
check_ty(self, t);
}
@ -48,110 +43,6 @@ pub fn check_crate(tcx: &ty::ctxt) {
tcx.sess.abort_if_errors();
}
// Yields the appropriate function to check the kind of closed over
// variables. `id` is the NodeId for some expression that creates the
// closure.
fn with_appropriate_checker(cx: &Context,
id: NodeId,
fn_span: Span,
b: |checker: |&Context, &ty::Freevar||) {
fn check_for_uniq(cx: &Context,
fn_span: Span,
fv: &ty::Freevar,
bounds: ty::BuiltinBounds) {
// all captured data must be owned, regardless of whether it is
// moved in or copied in.
let id = fv.def.def_id().node;
let var_t = ty::node_id_to_type(cx.tcx, id);
check_freevar_bounds(cx, fn_span, fv.span, var_t, bounds, None);
}
fn check_for_block(cx: &Context,
fn_span: Span,
fn_id: NodeId,
fv: &ty::Freevar,
bounds: ty::BuiltinBounds) {
let id = fv.def.def_id().node;
let var_t = ty::node_id_to_type(cx.tcx, id);
let upvar_id = ty::UpvarId { var_id: id, closure_expr_id: fn_id };
let upvar_borrow = cx.tcx.upvar_borrow(upvar_id);
let implicit_borrowed_type =
ty::mk_rptr(cx.tcx,
upvar_borrow.region,
ty::mt { mutbl: upvar_borrow.kind.to_mutbl_lossy(),
ty: var_t });
check_freevar_bounds(cx, fn_span, fv.span, implicit_borrowed_type,
bounds, Some(var_t));
}
fn check_for_bare(cx: &Context, fv: &ty::Freevar) {
span_err!(cx.tcx.sess, fv.span, E0143,
"can't capture dynamic environment in a fn item; \
use the || {} closure form instead", "{ ... }");
} // same check is done in resolve.rs, but shouldn't be done
let fty = ty::node_id_to_type(cx.tcx, id);
match ty::get(fty).sty {
ty::ty_closure(box ty::ClosureTy {
store: ty::UniqTraitStore,
bounds: bounds,
..
}) => {
b(|cx, fv| check_for_uniq(cx, fn_span, fv,
bounds.builtin_bounds))
}
ty::ty_closure(box ty::ClosureTy {
store: ty::RegionTraitStore(..), bounds, ..
}) => {
b(|cx, fv| check_for_block(cx, fn_span, id, fv,
bounds.builtin_bounds))
}
ty::ty_bare_fn(_) => {
b(check_for_bare)
}
ty::ty_unboxed_closure(..) => {}
ref s => {
cx.tcx.sess.bug(format!("expect fn type in kind checker, not \
{:?}",
s).as_slice());
}
}
}
// Check that the free variables used in a shared/sendable closure conform
// to the copy/move kind bounds. Then recursively check the function body.
fn check_fn(
cx: &mut Context,
fk: visit::FnKind,
decl: &FnDecl,
body: &Block,
sp: Span,
fn_id: NodeId) {
// <Check kinds on free variables:
with_appropriate_checker(cx, fn_id, sp, |chk| {
ty::with_freevars(cx.tcx, fn_id, |freevars| {
for fv in freevars.iter() {
chk(cx, fv);
}
});
});
match fk {
visit::FkFnBlock(..) => {
visit::walk_fn(cx, fk, decl, body, sp)
}
visit::FkItemFn(..) | visit::FkMethod(..) => {
visit::walk_fn(cx, fk, decl, body, sp);
}
}
}
fn check_ty(cx: &mut Context, aty: &Ty) {
match aty.node {
TyPath(_, _, id) => {
@ -208,29 +99,3 @@ pub fn check_typaram_bounds(cx: &Context,
});
}
pub fn check_freevar_bounds(cx: &Context, fn_span: Span, sp: Span, ty: ty::t,
bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
{
check_builtin_bounds(cx, ty, bounds, |missing| {
// Will be Some if the freevar is implicitly borrowed (stack closure).
// Emit a less mysterious error message in this case.
match referenced_ty {
Some(rty) => {
span_err!(cx.tcx.sess, sp, E0145,
"cannot implicitly borrow variable of type `{}` in a \
bounded stack closure (implicit reference does not fulfill `{}`)",
ty_to_string(cx.tcx, rty), missing.user_string(cx.tcx));
}
None => {
span_err!(cx.tcx.sess, sp, E0146,
"cannot capture variable of type `{}`, which does \
not fulfill `{}`, in a bounded closure",
ty_to_string(cx.tcx, ty), missing.user_string(cx.tcx));
}
}
span_note!(cx.tcx.sess, fn_span,
"this closure's environment must satisfy `{}`",
bounds.user_string(cx.tcx));
});
}

View file

@ -10,9 +10,9 @@
/*! See `doc.rs` for high-level documentation */
use super::DUMMY_CAUSE;
use super::{EvaluatedToMatch, EvaluatedToAmbiguity, EvaluatedToUnmatch};
use super::{evaluate_impl};
use super::ObligationCause;
use super::util;
use middle::subst;
@ -39,7 +39,7 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
// Determine whether `impl2` can provide an implementation for those
// same types.
let param_env = ty::empty_parameter_environment();
match evaluate_impl(infcx, &param_env, infcx.tcx, DUMMY_CAUSE,
match evaluate_impl(infcx, &param_env, infcx.tcx, ObligationCause::dummy(),
impl2_def_id, impl1_self_ty) {
EvaluatedToMatch | EvaluatedToAmbiguity => true,
EvaluatedToUnmatch => false,

View file

@ -77,11 +77,11 @@ pub enum ObligationCauseCode {
StructInitializerSized, // S { ... } must be Sized
VariableType(ast::NodeId), // Type of each variable must be Sized
RepeatVec, // [T,..n] --> T must be Copy
}
pub static DUMMY_CAUSE: ObligationCause =
ObligationCause { span: DUMMY_SP,
code: MiscObligation };
// Captures of variable the given id by a closure (span is the
// span of the closure)
ClosureCapture(ast::NodeId, Span)
}
pub type Obligations = subst::VecPerParamSpace<Obligation>;
@ -358,6 +358,10 @@ impl ObligationCause {
pub fn misc(span: Span) -> ObligationCause {
ObligationCause { span: span, code: MiscObligation }
}
pub fn dummy() -> ObligationCause {
ObligationCause { span: DUMMY_SP, code: MiscObligation }
}
}
impl<N> Vtable<N> {

View file

@ -120,11 +120,13 @@ and report an error, and it just seems like more mess in the end.)
use middle::def;
use middle::mem_categorization as mc;
use middle::traits;
use middle::ty::{ReScope};
use middle::ty;
use middle::typeck::astconv::AstConv;
use middle::typeck::check::FnCtxt;
use middle::typeck::check::regionmanip;
use middle::typeck::check::vtable2;
use middle::typeck::infer::resolve_and_force_all_but_regions;
use middle::typeck::infer::resolve_type;
use middle::typeck::infer;
@ -165,6 +167,11 @@ pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, blk: &ast::Block) {
// regionck assumes typeck succeeded
rcx.visit_fn_body(id, blk);
}
// Region checking a fn can introduce new trait obligations,
// particularly around closure bounds.
vtable2::select_all_fcx_obligations_or_error(fcx);
fcx.infcx().resolve_regions_and_report_errors();
}
@ -848,16 +855,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
}
});
}
ty::ty_closure(box ty::ClosureTy{store: ty::UniqTraitStore,
bounds: ref bounds,
..}) => {
// For proc, ensure that the *types* of the variables
// outlive region bound, since they are captured by value.
ty::with_freevars(tcx, expr.id, |freevars| {
ensure_free_variable_types_outlive_closure_bound(
rcx, bounds.region_bound, expr, freevars);
});
}
ty::ty_unboxed_closure(_, region) => {
ty::with_freevars(tcx, expr.id, |freevars| {
// No free variables means that there is no environment and
@ -868,8 +865,9 @@ fn check_expr_fn_block(rcx: &mut Rcx,
// NDM -- this seems wrong, discuss with pcwalton, should
// be straightforward enough.
if !freevars.is_empty() {
let bounds = ty::region_existential_bound(region);
ensure_free_variable_types_outlive_closure_bound(
rcx, region, expr, freevars);
rcx, bounds, expr, freevars);
}
})
}
@ -881,20 +879,26 @@ fn check_expr_fn_block(rcx: &mut Rcx,
rcx.set_repeating_scope(repeating_scope);
match ty::get(function_type).sty {
ty::ty_closure(box ty::ClosureTy {
store: ty::RegionTraitStore(..),
..
}) => {
ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. }) => {
ty::with_freevars(tcx, expr.id, |freevars| {
propagate_upupvar_borrow_kind(rcx, expr, freevars);
})
}
_ => ()
_ => {}
}
match ty::get(function_type).sty {
ty::ty_closure(box ty::ClosureTy {bounds, ..}) => {
ty::with_freevars(tcx, expr.id, |freevars| {
ensure_free_variable_types_outlive_closure_bound(rcx, bounds, expr, freevars);
})
}
_ => {}
}
fn ensure_free_variable_types_outlive_closure_bound(
rcx: &mut Rcx,
region_bound: ty::Region,
bounds: ty::ExistentialBounds,
expr: &ast::Expr,
freevars: &[ty::Freevar])
{
@ -908,7 +912,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
let tcx = rcx.fcx.ccx.tcx;
debug!("ensure_free_variable_types_outlive_closure_bound({}, {})",
region_bound.repr(tcx), expr.repr(tcx));
bounds.region_bound.repr(tcx), expr.repr(tcx));
for freevar in freevars.iter() {
let var_node_id = {
@ -917,11 +921,35 @@ fn check_expr_fn_block(rcx: &mut Rcx,
def_id.node
};
let var_ty = rcx.resolve_node_type(var_node_id);
// Compute the type of the field in the environment that
// represents `var_node_id`. For a by-value closure, this
// will be the same as the type of the variable. For a
// by-reference closure, this will be `&T` where `T` is
// the type of the variable.
let raw_var_ty = rcx.resolve_node_type(var_node_id);
let upvar_id = ty::UpvarId { var_id: var_node_id,
closure_expr_id: expr.id };
let var_ty = match rcx.fcx.inh.upvar_borrow_map.borrow().find(&upvar_id) {
Some(upvar_borrow) => {
ty::mk_rptr(rcx.tcx(),
upvar_borrow.region,
ty::mt { mutbl: upvar_borrow.kind.to_mutbl_lossy(),
ty: raw_var_ty })
}
None => raw_var_ty
};
// Check that the type meets the criteria of the existential bounds:
for builtin_bound in bounds.builtin_bounds.iter() {
let code = traits::ClosureCapture(var_node_id, expr.span);
let cause = traits::ObligationCause::new(freevar.span, code);
let obligation = traits::obligation_for_builtin_bound(rcx.tcx(), cause,
var_ty, builtin_bound);
rcx.fcx.inh.fulfillment_cx.borrow_mut().register_obligation(rcx.tcx(), obligation);
}
type_must_outlive(
rcx, infer::RelateProcBound(expr.span, var_node_id, var_ty),
var_ty, region_bound);
var_ty, bounds.region_bound);
}
}

View file

@ -398,5 +398,13 @@ fn note_obligation_cause(fcx: &FnCtxt,
"use \"#[unsafe_destructor]\" on the implementation \
to force the compiler to allow this");
}
traits::ClosureCapture(var_id, closure_span) => {
let name = ty::local_var_name_str(tcx, var_id);
span_note!(tcx.sess, closure_span,
"the closure that captures `{}` requires that all captured variables \"
implement the trait `{}`",
name,
trait_name);
}
}
}

View file

@ -1560,7 +1560,7 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
"...so that it can be closed over into an object");
}
infer::RelateProcBound(span, var_node_id, _ty) => {
self.tcx.sess.span_err(
self.tcx.sess.span_note(
span,
format!(
"...so that the variable `{}` can be captured \

View file

@ -12,8 +12,9 @@ fn bar(blk: ||:'static) {
}
fn foo(x: &()) {
bar(|| {
let _ = x; //~ ERROR captured variable `x` does not outlive
bar(|| { //~ ERROR cannot infer an appropriate lifetime
let _ = x;
//~^ ERROR captured variable `x` does not outlive
})
}

View file

@ -15,8 +15,8 @@ fn foo(_x: Gc<uint>) {}
fn main() {
let x = box(GC) 3u;
let _: proc():Send = proc() foo(x); //~ ERROR does not fulfill `Send`
let _: proc():Send = proc() foo(x); //~ ERROR does not fulfill `Send`
let _: proc():Send = proc() foo(x); //~ ERROR does not fulfill `Send`
let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented
let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented
let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented
let _: proc() = proc() foo(x);
}

View file

@ -37,7 +37,7 @@ fn main() {
task::spawn(proc() {
let y = x;
//~^ ERROR does not fulfill `Send`
//~^ ERROR `core::kinds::Send` is not implemented
println!("{:?}", y);
});
}