diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs index 1a2d21baa210a..48e6650693555 100644 --- a/src/librustc/middle/freevars.rs +++ b/src/librustc/middle/freevars.rs @@ -24,7 +24,7 @@ use syntax::codemap::Span; use syntax::visit::Visitor; use syntax::visit; -#[deriving(Clone, Decodable, Encodable, Show)] +#[deriving(Clone, Decodable, Encodable, PartialEq, Eq, Show)] pub enum CaptureMode { /// Copy/move the value from this llvm ValueRef into the environment. CaptureByValue, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ae96937757f0e..f44bc18972602 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2254,6 +2254,8 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { // FIXME(#14449): `borrowed_contents` below assumes `&mut` // unboxed closure. let upvars = unboxed_closure_upvars(cx, did); + debug!("region of unboxed closure is {}", + r.repr(cx)); TypeContents::union(upvars.as_slice(), |f| tc_ty(cx, f.ty, cache)) | borrowed_contents(r, MutMutable) diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 8e41c9463f6b8..46b5197b71718 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -666,14 +666,23 @@ fn check_expr_fn_block(rcx: &mut Rcx, }); } ty::ty_unboxed_closure(_, region) => { + debug!("regionck: constraining region of unboxed closure"); freevars::with_freevars(tcx, expr.id, |freevars| { - // No free variables means that there is no environment and - // hence the closure has static lifetime. Otherwise, the - // closure must not outlive the variables it closes over - // by-reference. - if !freevars.is_empty() { - constrain_free_variables(rcx, region, expr, freevars); - } + if freevars.is_empty() { + // No free variables means that there is no + // environment and hence the closure has static + // lifetime. Otherwise, the closure must not outlive + // the variables it closes over by-reference. + rcx.fcx.mk_subr(false, + infer::MiscRegion(expr.span), + ty::ReStatic, + region); + } else { + constrain_free_variables(rcx, + region, + expr, + freevars); + } }) } _ => () @@ -709,6 +718,8 @@ fn check_expr_fn_block(rcx: &mut Rcx, let infcx = rcx.fcx.infcx(); debug!("constrain_free_variables({}, {})", region.repr(tcx), expr.repr(tcx)); + let capture_mode = freevars::get_capture_mode(tcx, expr.id); + let mut all_static = capture_mode == freevars::CaptureByValue; for freevar in freevars.iter() { debug!("freevar def is {:?}", freevar.def); @@ -716,29 +727,70 @@ fn check_expr_fn_block(rcx: &mut Rcx, let def = freevar.def; let def_id = def.def_id(); assert!(def_id.krate == ast::LOCAL_CRATE); - let upvar_id = ty::UpvarId { var_id: def_id.node, - closure_expr_id: expr.id }; - - // Create a region variable to represent this borrow. This borrow - // must outlive the region on the closure. - let origin = infer::UpvarRegion(upvar_id, expr.span); - let freevar_region = infcx.next_region_var(origin); - rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), - region, freevar_region); - - // Create a UpvarBorrow entry. Note that we begin with a - // const borrow_kind, but change it to either mut or - // immutable as dictated by the uses. - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, - region: freevar_region }; - rcx.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id, - upvar_borrow); - - // Guarantee that the closure does not outlive the variable itself. - let en_region = region_of_def(rcx.fcx, def); - debug!("en_region = {}", en_region.repr(tcx)); - rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), - region, en_region); + let upvar_id = ty::UpvarId { + var_id: def_id.node, + closure_expr_id: expr.id, + }; + + if capture_mode == freevars::CaptureByRef { + // Create a region variable to represent this borrow. This + // borrow must outlive the region on the closure. + let origin = infer::UpvarRegion(upvar_id, expr.span); + let freevar_region = infcx.next_region_var(origin); + rcx.fcx.mk_subr(true, + infer::FreeVariable(freevar.span, + def_id.node), + region, + freevar_region); + + // Create a UpvarBorrow entry. Note that we begin with a + // const borrow_kind, but change it to either mut or + // immutable as dictated by the uses. + let upvar_borrow = ty::UpvarBorrow { + kind: ty::ImmBorrow, + region: freevar_region, + }; + rcx.fcx + .inh + .upvar_borrow_map + .borrow_mut() + .insert(upvar_id, upvar_borrow); + + // Guarantee that the closure does not outlive the variable + // itself. + let en_region = region_of_def(rcx.fcx, def); + debug!("en_region = {}", en_region.repr(tcx)); + rcx.fcx.mk_subr(true, + infer::FreeVariable(freevar.span, + def_id.node), + region, + en_region); + } + + // FIXME(pcwalton): As a hack, if the type of the variable being + // closed over has no regions and this is a by-value capture, + // record a `'static` bound. + let local_type = rcx.fcx.local_ty(freevar.span, def_id.node); + let local_type = + rcx.fcx.infcx().resolve_type_vars_if_possible(local_type); + if all_static && !ty::type_is_static(rcx.tcx(), local_type) { + debug!("regionck: not static: {}", + rcx.fcx + .local_ty(freevar.span, def_id.node) + .repr(rcx.tcx())); + all_static = false + } + } + + // FIXME(pcwalton): As a hack, if the type of the variable being + // closed over has no regions and this is a by-value capture, + // record a `'static` bound. + if all_static { + debug!("regionck: all static!"); + rcx.fcx.mk_subr(false, + infer::MiscRegion(expr.span), + ty::ReStatic, + region); } } @@ -1022,9 +1074,14 @@ fn constrain_regions_in_type_of_node( rcx.fcx.inh.adjustments.borrow().find(&id), |method_call| rcx.resolve_method_type(method_call)); debug!("constrain_regions_in_type_of_node(\ - ty={}, ty0={}, id={}, minimum_lifetime={:?})", + ty={},\ + ty0={},\ + id={},\ + minimum_lifetime={:?},\ + origin={:?})", ty_to_string(tcx, ty), ty_to_string(tcx, ty0), - id, minimum_lifetime); + id, minimum_lifetime, + origin); constrain_regions_in_type(rcx, minimum_lifetime, origin, ty); } diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index beaf81409a38b..362fc729da462 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -620,6 +620,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sup, ""); } + infer::MiscRegion(span) => { + self.tcx.sess.span_err(span, "reference is not valid"); + note_and_explain_region( + self.tcx, + "the reference is valid for ", + sub, + ""); + note_and_explain_region( + self.tcx, + "but the referenced data is only valid for ", + sup, + ""); + } } } @@ -1425,6 +1438,9 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { "...so that the pointer does not outlive the \ data it points at"); } + infer::MiscRegion(span) => { + self.tcx.sess.span_note(span, "...so that reference is valid") + } } } } diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 30fffc42a3f97..0393871f9e410 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -194,6 +194,9 @@ pub enum SubregionOrigin { // An auto-borrow that does not enclose the expr where it occurs AutoBorrow(Span), + + // Not yet categorized + MiscRegion(Span), } /// Reasons to create a region inference variable @@ -905,6 +908,7 @@ impl SubregionOrigin { CallReturn(a) => a, AddrOf(a) => a, AutoBorrow(a) => a, + MiscRegion(a) => a, } } } @@ -948,6 +952,7 @@ impl Repr for SubregionOrigin { CallReturn(a) => format!("CallReturn({})", a.repr(tcx)), AddrOf(a) => format!("AddrOf({})", a.repr(tcx)), AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)), + MiscRegion(a) => format!("MiscRegion({})", a.repr(tcx)), } } } diff --git a/src/test/run-pass/unboxed-closures-send.rs b/src/test/run-pass/unboxed-closures-send.rs new file mode 100644 index 0000000000000..e80c19339efd0 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-send.rs @@ -0,0 +1,40 @@ +// Copyright 2014 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. + +#![feature(unboxed_closures)] + +use std::mem; +use std::ops::Fn; + +// Unsugared closure +struct Closure(u8); + +impl Fn<(u8,), u8> for Closure { + extern "rust-call" fn call(&self, (y,): (u8,)) -> u8 { + let &Closure(x) = self; + + x + y + } +} + +fn main() { + let y = 0u8; + let closure = |&: x: u8| x + y; + let unsugared_closure = Closure(y); + + // Check that both closures are capturing by value + println!("{}", mem::size_of_val(&closure)); // prints 1 + println!("{}", mem::size_of_val(&unsugared_closure)); // prints 1 + + spawn(proc() { + let ok = unsugared_closure; + let err = closure; + }) +}