Skip to content

Commit 0806c0a

Browse files
committed
Add test for clone count of .use in non-move closure
1 parent 0a73b5d commit 0806c0a

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//! Regression test for #157141.
2+
//!
3+
//! A non-`move`, non-`use` closure that only `.use`s an upvar should capture it
4+
//! by immutable borrow, not by `use` (which would clone the value into the
5+
//! closure at construction time). The `.use` expression in the body is then the
6+
//! only thing that clones, once per evaluation.
7+
8+
//@ run-pass
9+
10+
#![feature(ergonomic_clones)]
11+
#![allow(incomplete_features)]
12+
13+
use std::sync::atomic::{AtomicUsize, Ordering};
14+
15+
static CLONES: AtomicUsize = AtomicUsize::new(0);
16+
17+
struct Thing;
18+
19+
impl Clone for Thing {
20+
fn clone(&self) -> Self {
21+
CLONES.fetch_add(1, Ordering::Relaxed);
22+
Thing
23+
}
24+
}
25+
26+
impl std::clone::UseCloned for Thing {}
27+
28+
fn clones_during<R>(f: impl FnOnce() -> R) -> usize {
29+
let before = CLONES.load(Ordering::Relaxed);
30+
f();
31+
CLONES.load(Ordering::Relaxed) - before
32+
}
33+
34+
fn main() {
35+
// Plain `||` closure: `x` is borrowed, so building the closure clones nothing.
36+
// Each call clones exactly once via the `.use` in the body.
37+
let x = Thing;
38+
let n = clones_during(|| {
39+
let closure = || {
40+
let _y = x.use;
41+
};
42+
closure();
43+
closure();
44+
});
45+
assert_eq!(n, 2, "plain `||` closure should clone once per call, not also at capture");
46+
drop(x);
47+
48+
// `move ||` closure: `x` is moved in (no capture clone), `.use` clones per call.
49+
let x = Thing;
50+
let n = clones_during(|| {
51+
let closure = move || {
52+
let _y = x.use;
53+
};
54+
closure();
55+
closure();
56+
});
57+
assert_eq!(n, 2, "`move ||` closure should clone once per call");
58+
59+
// `use ||` closure: the capture clause clones `x` into the closure once, at
60+
// construction. Calling it afterwards moves the captured value out.
61+
let x = Thing;
62+
let n = clones_during(|| {
63+
let closure = use || {
64+
let _y = &x;
65+
};
66+
closure();
67+
});
68+
assert_eq!(n, 1, "`use ||` closure clones once at construction");
69+
}

0 commit comments

Comments
 (0)