Skip to content

Commit

Permalink
Rollup merge of rust-lang#49299 - SimonSapin:ubiquity, r=nikomatsakis
Browse files Browse the repository at this point in the history
Stabilize the copy_closures and clone_closures features

In addition to the `Fn*` family of traits, closures now implement `Copy` (and similarly `Clone`) if all of the captures do.

Tracking issue: rust-lang#44490
  • Loading branch information
kennytm committed Mar 24, 2018
2 parents 5454451 + 1efe0b3 commit 297a6e5
Show file tree
Hide file tree
Showing 24 changed files with 26 additions and 171 deletions.
5 changes: 5 additions & 0 deletions src/libcore/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
/// This trait can be used with `#[derive]` if all fields are `Clone`. The `derive`d
/// implementation of [`clone`] calls [`clone`] on each field.
///
/// ## Closures
///
/// Closure types automatically implement `Clone` if they capture no value from the environment
/// or if all such captured values implement `Clone` themselves.
///
/// ## How can I implement `Clone`?
///
/// Types that are [`Copy`] should have a trivial implementation of `Clone`. More formally:
Expand Down
5 changes: 5 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ pub trait Unsize<T: ?Sized> {
/// are allowed to access `x` after the assignment. Under the hood, both a copy and a move
/// can result in bits being copied in memory, although this is sometimes optimized away.
///
/// ## Closures
///
/// Closure types automatically implement `Copy` if they capture no value from the environment
/// or if all such captured values implement `Copy` themselves.
///
/// ## How can I implement `Copy`?
///
/// There are two ways to implement `Copy` on your type. The simplest is to use `derive`:
Expand Down
2 changes: 0 additions & 2 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,6 @@ define_dep_nodes!( <'tcx>
[input] MissingExternCrateItem(CrateNum),
[input] UsedCrateSource(CrateNum),
[input] PostorderCnums,
[] HasCloneClosures(CrateNum),
[] HasCopyClosures(CrateNum),

// This query is not expected to have inputs -- as a result, it's
// not a good candidate for "replay" because it's essentially a
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
#![feature(box_syntax)]
#![feature(conservative_impl_trait)]
#![feature(const_fn)]
#![feature(copy_closures, clone_closures)]
#![cfg_attr(stage0, feature(copy_closures, clone_closures))]
#![feature(core_intrinsics)]
#![feature(drain_filter)]
#![feature(dyn_trait)]
Expand Down
11 changes: 3 additions & 8 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2086,14 +2086,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {

ty::TyClosure(def_id, substs) => {
let trait_id = obligation.predicate.def_id();
let copy_closures =
Some(trait_id) == self.tcx().lang_items().copy_trait() &&
self.tcx().has_copy_closures(def_id.krate);
let clone_closures =
Some(trait_id) == self.tcx().lang_items().clone_trait() &&
self.tcx().has_clone_closures(def_id.krate);

if copy_closures || clone_closures {
let is_copy_trait = Some(trait_id) == self.tcx().lang_items().copy_trait();
let is_clone_trait = Some(trait_id) == self.tcx().lang_items().clone_trait();
if is_copy_trait || is_clone_trait {
Where(ty::Binder(substs.upvar_tys(def_id, self.tcx()).collect()))
} else {
Never
Expand Down
8 changes: 0 additions & 8 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2562,14 +2562,6 @@ pub fn provide(providers: &mut ty::maps::Providers) {
assert_eq!(cnum, LOCAL_CRATE);
tcx.output_filenames.clone()
};
providers.has_copy_closures = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
tcx.features().copy_closures
};
providers.has_clone_closures = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
tcx.features().clone_closures
};
providers.features_query = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
Lrc::new(tcx.sess.features_untracked().clone())
Expand Down
12 changes: 0 additions & 12 deletions src/librustc/ty/maps/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,24 +610,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::output_filenames<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::has_clone_closures<'tcx> {
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
format!("seeing if the crate has enabled `Clone` closures")
}
}

impl<'tcx> QueryDescription<'tcx> for queries::vtable_methods<'tcx> {
fn describe(tcx: TyCtxt, key: ty::PolyTraitRef<'tcx> ) -> String {
format!("finding all methods for trait {}", tcx.item_path_str(key.def_id()))
}
}

impl<'tcx> QueryDescription<'tcx> for queries::has_copy_closures<'tcx> {
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
format!("seeing if the crate has enabled `Copy` closures")
}
}

impl<'tcx> QueryDescription<'tcx> for queries::features_query<'tcx> {
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
format!("looking up enabled feature gates")
Expand Down
3 changes: 0 additions & 3 deletions src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,6 @@ define_maps! { <'tcx>
[] fn output_filenames: output_filenames_node(CrateNum)
-> Arc<OutputFilenames>,

[] fn has_copy_closures: HasCopyClosures(CrateNum) -> bool,
[] fn has_clone_closures: HasCloneClosures(CrateNum) -> bool,

// Erases regions from `ty` to yield a new type.
// Normally you would just use `tcx.erase_regions(&value)`,
// however, which uses this query as a kind of cache.
Expand Down
2 changes: 0 additions & 2 deletions src/librustc/ty/maps/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,8 +920,6 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
}
DepKind::UsedCrateSource => { force!(used_crate_source, krate!()); }
DepKind::PostorderCnums => { force!(postorder_cnums, LOCAL_CRATE); }
DepKind::HasCloneClosures => { force!(has_clone_closures, krate!()); }
DepKind::HasCopyClosures => { force!(has_copy_closures, krate!()); }

DepKind::Freevars => { force!(freevars, def_id!()); }
DepKind::MaybeUnusedTraitImport => {
Expand Down
10 changes: 0 additions & 10 deletions src/librustc_metadata/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,16 +226,6 @@ impl CrateMetadata {
attr::contains_name(&attrs, "no_builtins")
}

pub fn has_copy_closures(&self, sess: &Session) -> bool {
let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess);
attr::contains_feature_attr(&attrs, "copy_closures")
}

pub fn has_clone_closures(&self, sess: &Session) -> bool {
let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess);
attr::contains_feature_attr(&attrs, "clone_closures")
}

pub fn panic_strategy(&self) -> PanicStrategy {
self.root.panic_strategy.clone()
}
Expand Down
3 changes: 0 additions & 3 deletions src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,6 @@ provide! { <'tcx> tcx, def_id, other, cdata,

used_crate_source => { Lrc::new(cdata.source.clone()) }

has_copy_closures => { cdata.has_copy_closures(tcx.sess) }
has_clone_closures => { cdata.has_clone_closures(tcx.sess) }

exported_symbols => {
let cnum = cdata.cnum;
assert!(cnum != LOCAL_CRATE);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ This API is completely unstable and subject to change.
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(conservative_impl_trait)]
#![feature(copy_closures, clone_closures)]
#![cfg_attr(stage0, feature(copy_closures, clone_closures))]
#![feature(crate_visibility_modifier)]
#![feature(from_ref)]
#![feature(match_default_bindings)]
Expand Down
26 changes: 3 additions & 23 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,6 @@ declare_features! (
// Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008)
(active, non_exhaustive, "1.22.0", Some(44109), None),

// Copy/Clone closures (RFC 2132)
(active, clone_closures, "1.22.0", Some(44490), None),
(active, copy_closures, "1.22.0", Some(44490), None),

// allow `'_` placeholder lifetimes
(active, underscore_lifetimes, "1.22.0", Some(44524), None),

Expand Down Expand Up @@ -567,6 +563,9 @@ declare_features! (
(accepted, dotdoteq_in_patterns, "1.26.0", Some(28237), None),
// Termination trait in main (RFC 1937)
(accepted, termination_trait, "1.26.0", Some(43301), None),
// Copy/Clone closures (RFC 2132)
(accepted, clone_closures, "1.26.0", Some(44490), None),
(accepted, copy_closures, "1.26.0", Some(44490), None),
);

// If you change this, please modify src/doc/unstable-book as well. You must
Expand Down Expand Up @@ -1887,8 +1886,6 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
struct FeatureChecker {
proc_macro: Option<Span>,
custom_attribute: Option<Span>,
copy_closures: Option<Span>,
clone_closures: Option<Span>,
}

impl FeatureChecker {
Expand All @@ -1904,14 +1901,6 @@ impl FeatureChecker {
if features.custom_attribute {
self.custom_attribute = self.custom_attribute.or(Some(span));
}

if features.copy_closures {
self.copy_closures = self.copy_closures.or(Some(span));
}

if features.clone_closures {
self.clone_closures = self.clone_closures.or(Some(span));
}
}

fn check(self, handler: &Handler) {
Expand All @@ -1923,15 +1912,6 @@ impl FeatureChecker {

FatalError.raise();
}

if let (Some(span), None) = (self.copy_closures, self.clone_closures) {
handler.struct_span_err(span, "`#![feature(copy_closures)]` can only be used with \
`#![feature(clone_closures)]`")
.span_note(span, "`#![feature(copy_closures)]` declared here")
.emit();

FatalError.raise();
}
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/test/compile-fail/not-clone-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

// Check that closures do not implement `Clone` if their environment is not `Clone`.

#![feature(clone_closures)]

struct S(i32);

fn main() {
Expand Down
3 changes: 0 additions & 3 deletions src/test/compile-fail/not-copy-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@

// Check that closures do not implement `Copy` if their environment is not `Copy`.

#![feature(copy_closures)]
#![feature(clone_closures)]

fn main() {
let mut a = 5;
let hello = || {
Expand Down
19 changes: 0 additions & 19 deletions src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/test/run-pass/clone-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

// Check that closures implement `Clone`.

#![feature(clone_closures)]

#[derive(Clone)]
struct S(i32);

Expand Down
3 changes: 0 additions & 3 deletions src/test/run-pass/copy-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@

// Check that closures implement `Copy`.

#![feature(copy_closures)]
#![feature(clone_closures)]

fn call<T, F: FnOnce() -> T>(f: F) -> T { f() }

fn main() {
Expand Down
21 changes: 0 additions & 21 deletions src/test/ui/feature-gate-clone-closures.rs

This file was deleted.

11 changes: 0 additions & 11 deletions src/test/ui/feature-gate-clone-closures.stderr

This file was deleted.

19 changes: 0 additions & 19 deletions src/test/ui/feature-gate-copy-closures.rs

This file was deleted.

13 changes: 0 additions & 13 deletions src/test/ui/feature-gate-copy-closures.stderr

This file was deleted.

5 changes: 4 additions & 1 deletion src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ fn test6() {

fn test7() {
fn foo<F>(_: F) where F: FnMut(Box<FnMut(isize)>, isize) {}
let mut f = |g: Box<FnMut(isize)>, b: isize| {};
let s = String::new(); // Capture to make f !Copy
let mut f = move |g: Box<FnMut(isize)>, b: isize| {
let _ = s.len();
};
f(Box::new(|a| {
foo(f);
//~^ ERROR cannot move `f` into closure because it is borrowed
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ LL | f.f.call_mut(())
| ^^^ cannot borrow as mutable

error[E0504]: cannot move `f` into closure because it is borrowed
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:63:13
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:66:13
|
LL | f(Box::new(|a| {
| - borrow of `f` occurs here
LL | foo(f);
| ^ move into closure occurs here

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:63:13
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:66:13
|
LL | let mut f = |g: Box<FnMut(isize)>, b: isize| {};
LL | let mut f = move |g: Box<FnMut(isize)>, b: isize| {
| ----- captured outer variable
LL | f(Box::new(|a| {
...
LL | foo(f);
| ^ cannot move out of captured outer variable in an `FnMut` closure

Expand Down

0 comments on commit 297a6e5

Please sign in to comment.