Skip to content

librustc: Add a hack to allow unboxed closures to achieve the 'static #16622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/middle/freevars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
121 changes: 89 additions & 32 deletions src/librustc/middle/typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
})
}
_ => ()
Expand Down Expand Up @@ -709,36 +718,79 @@ 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);

// Identify the variable being closed over and its node-id.
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);
}
}

Expand Down Expand Up @@ -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);
}

Expand Down
16 changes: 16 additions & 0 deletions src/librustc/middle/typeck/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
"");
}
}
}

Expand Down Expand Up @@ -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")
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/middle/typeck/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -905,6 +908,7 @@ impl SubregionOrigin {
CallReturn(a) => a,
AddrOf(a) => a,
AutoBorrow(a) => a,
MiscRegion(a) => a,
}
}
}
Expand Down Expand Up @@ -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)),
}
}
}
Expand Down
40 changes: 40 additions & 0 deletions src/test/run-pass/unboxed-closures-send.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
})
}