Skip to content

Commit eb43525

Browse files
committed
librustc: Add a hack to allow unboxed closures to achieve the 'static
bound if possible. Closes #16560.
1 parent 3f5d0b5 commit eb43525

File tree

6 files changed

+153
-33
lines changed

6 files changed

+153
-33
lines changed

src/librustc/middle/freevars.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use syntax::codemap::Span;
2424
use syntax::visit::Visitor;
2525
use syntax::visit;
2626

27-
#[deriving(Clone, Decodable, Encodable, Show)]
27+
#[deriving(Clone, Decodable, Encodable, PartialEq, Eq, Show)]
2828
pub enum CaptureMode {
2929
/// Copy/move the value from this llvm ValueRef into the environment.
3030
CaptureByValue,

src/librustc/middle/ty.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,6 +2254,8 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
22542254
// FIXME(#14449): `borrowed_contents` below assumes `&mut`
22552255
// unboxed closure.
22562256
let upvars = unboxed_closure_upvars(cx, did);
2257+
debug!("region of unboxed closure is {}",
2258+
r.repr(cx));
22572259
TypeContents::union(upvars.as_slice(),
22582260
|f| tc_ty(cx, f.ty, cache)) |
22592261
borrowed_contents(r, MutMutable)

src/librustc/middle/typeck/check/regionck.rs

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -666,14 +666,23 @@ fn check_expr_fn_block(rcx: &mut Rcx,
666666
});
667667
}
668668
ty::ty_unboxed_closure(_, region) => {
669+
debug!("regionck: constraining region of unboxed closure");
669670
freevars::with_freevars(tcx, expr.id, |freevars| {
670-
// No free variables means that there is no environment and
671-
// hence the closure has static lifetime. Otherwise, the
672-
// closure must not outlive the variables it closes over
673-
// by-reference.
674-
if !freevars.is_empty() {
675-
constrain_free_variables(rcx, region, expr, freevars);
676-
}
671+
if freevars.is_empty() {
672+
// No free variables means that there is no
673+
// environment and hence the closure has static
674+
// lifetime. Otherwise, the closure must not outlive
675+
// the variables it closes over by-reference.
676+
rcx.fcx.mk_subr(false,
677+
infer::MiscRegion(expr.span),
678+
ty::ReStatic,
679+
region);
680+
} else {
681+
constrain_free_variables(rcx,
682+
region,
683+
expr,
684+
freevars);
685+
}
677686
})
678687
}
679688
_ => ()
@@ -709,36 +718,79 @@ fn check_expr_fn_block(rcx: &mut Rcx,
709718
let infcx = rcx.fcx.infcx();
710719
debug!("constrain_free_variables({}, {})",
711720
region.repr(tcx), expr.repr(tcx));
721+
let capture_mode = freevars::get_capture_mode(tcx, expr.id);
722+
let mut all_static = capture_mode == freevars::CaptureByValue;
712723
for freevar in freevars.iter() {
713724
debug!("freevar def is {:?}", freevar.def);
714725

715726
// Identify the variable being closed over and its node-id.
716727
let def = freevar.def;
717728
let def_id = def.def_id();
718729
assert!(def_id.krate == ast::LOCAL_CRATE);
719-
let upvar_id = ty::UpvarId { var_id: def_id.node,
720-
closure_expr_id: expr.id };
721-
722-
// Create a region variable to represent this borrow. This borrow
723-
// must outlive the region on the closure.
724-
let origin = infer::UpvarRegion(upvar_id, expr.span);
725-
let freevar_region = infcx.next_region_var(origin);
726-
rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node),
727-
region, freevar_region);
728-
729-
// Create a UpvarBorrow entry. Note that we begin with a
730-
// const borrow_kind, but change it to either mut or
731-
// immutable as dictated by the uses.
732-
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
733-
region: freevar_region };
734-
rcx.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
735-
upvar_borrow);
736-
737-
// Guarantee that the closure does not outlive the variable itself.
738-
let en_region = region_of_def(rcx.fcx, def);
739-
debug!("en_region = {}", en_region.repr(tcx));
740-
rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node),
741-
region, en_region);
730+
let upvar_id = ty::UpvarId {
731+
var_id: def_id.node,
732+
closure_expr_id: expr.id,
733+
};
734+
735+
if capture_mode == freevars::CaptureByRef {
736+
// Create a region variable to represent this borrow. This
737+
// borrow must outlive the region on the closure.
738+
let origin = infer::UpvarRegion(upvar_id, expr.span);
739+
let freevar_region = infcx.next_region_var(origin);
740+
rcx.fcx.mk_subr(true,
741+
infer::FreeVariable(freevar.span,
742+
def_id.node),
743+
region,
744+
freevar_region);
745+
746+
// Create a UpvarBorrow entry. Note that we begin with a
747+
// const borrow_kind, but change it to either mut or
748+
// immutable as dictated by the uses.
749+
let upvar_borrow = ty::UpvarBorrow {
750+
kind: ty::ImmBorrow,
751+
region: freevar_region,
752+
};
753+
rcx.fcx
754+
.inh
755+
.upvar_borrow_map
756+
.borrow_mut()
757+
.insert(upvar_id, upvar_borrow);
758+
759+
// Guarantee that the closure does not outlive the variable
760+
// itself.
761+
let en_region = region_of_def(rcx.fcx, def);
762+
debug!("en_region = {}", en_region.repr(tcx));
763+
rcx.fcx.mk_subr(true,
764+
infer::FreeVariable(freevar.span,
765+
def_id.node),
766+
region,
767+
en_region);
768+
}
769+
770+
// FIXME(pcwalton): As a hack, if the type of the variable being
771+
// closed over has no regions and this is a by-value capture,
772+
// record a `'static` bound.
773+
let local_type = rcx.fcx.local_ty(freevar.span, def_id.node);
774+
let local_type =
775+
rcx.fcx.infcx().resolve_type_vars_if_possible(local_type);
776+
if all_static && !ty::type_is_static(rcx.tcx(), local_type) {
777+
debug!("regionck: not static: {}",
778+
rcx.fcx
779+
.local_ty(freevar.span, def_id.node)
780+
.repr(rcx.tcx()));
781+
all_static = false
782+
}
783+
}
784+
785+
// FIXME(pcwalton): As a hack, if the type of the variable being
786+
// closed over has no regions and this is a by-value capture,
787+
// record a `'static` bound.
788+
if all_static {
789+
debug!("regionck: all static!");
790+
rcx.fcx.mk_subr(false,
791+
infer::MiscRegion(expr.span),
792+
ty::ReStatic,
793+
region);
742794
}
743795
}
744796

@@ -1022,9 +1074,14 @@ fn constrain_regions_in_type_of_node(
10221074
rcx.fcx.inh.adjustments.borrow().find(&id),
10231075
|method_call| rcx.resolve_method_type(method_call));
10241076
debug!("constrain_regions_in_type_of_node(\
1025-
ty={}, ty0={}, id={}, minimum_lifetime={:?})",
1077+
ty={},\
1078+
ty0={},\
1079+
id={},\
1080+
minimum_lifetime={:?},\
1081+
origin={:?})",
10261082
ty_to_string(tcx, ty), ty_to_string(tcx, ty0),
1027-
id, minimum_lifetime);
1083+
id, minimum_lifetime,
1084+
origin);
10281085
constrain_regions_in_type(rcx, minimum_lifetime, origin, ty);
10291086
}
10301087

src/librustc/middle/typeck/infer/error_reporting.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
620620
sup,
621621
"");
622622
}
623+
infer::MiscRegion(span) => {
624+
self.tcx.sess.span_err(span, "reference is not valid");
625+
note_and_explain_region(
626+
self.tcx,
627+
"the reference is valid for ",
628+
sub,
629+
"");
630+
note_and_explain_region(
631+
self.tcx,
632+
"but the referenced data is only valid for ",
633+
sup,
634+
"");
635+
}
623636
}
624637
}
625638

@@ -1425,6 +1438,9 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> {
14251438
"...so that the pointer does not outlive the \
14261439
data it points at");
14271440
}
1441+
infer::MiscRegion(span) => {
1442+
self.tcx.sess.span_note(span, "...so that reference is valid")
1443+
}
14281444
}
14291445
}
14301446
}

src/librustc/middle/typeck/infer/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ pub enum SubregionOrigin {
194194

195195
// An auto-borrow that does not enclose the expr where it occurs
196196
AutoBorrow(Span),
197+
198+
// Not yet categorized
199+
MiscRegion(Span),
197200
}
198201

199202
/// Reasons to create a region inference variable
@@ -905,6 +908,7 @@ impl SubregionOrigin {
905908
CallReturn(a) => a,
906909
AddrOf(a) => a,
907910
AutoBorrow(a) => a,
911+
MiscRegion(a) => a,
908912
}
909913
}
910914
}
@@ -948,6 +952,7 @@ impl Repr for SubregionOrigin {
948952
CallReturn(a) => format!("CallReturn({})", a.repr(tcx)),
949953
AddrOf(a) => format!("AddrOf({})", a.repr(tcx)),
950954
AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)),
955+
MiscRegion(a) => format!("MiscRegion({})", a.repr(tcx)),
951956
}
952957
}
953958
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(unboxed_closures)]
12+
13+
use std::mem;
14+
use std::ops::Fn;
15+
16+
// Unsugared closure
17+
struct Closure(u8);
18+
19+
impl Fn<(u8,), u8> for Closure {
20+
extern "rust-call" fn call(&self, (y,): (u8,)) -> u8 {
21+
let &Closure(x) = self;
22+
23+
x + y
24+
}
25+
}
26+
27+
fn main() {
28+
let y = 0u8;
29+
let closure = |&: x: u8| x + y;
30+
let unsugared_closure = Closure(y);
31+
32+
// Check that both closures are capturing by value
33+
println!("{}", mem::size_of_val(&closure)); // prints 1
34+
println!("{}", mem::size_of_val(&unsugared_closure)); // prints 1
35+
36+
spawn(proc() {
37+
let ok = unsugared_closure;
38+
let err = closure;
39+
})
40+
}

0 commit comments

Comments
 (0)