|
| 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