Skip to content

[WIP] testing #111263

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
32 changes: 32 additions & 0 deletions library/core/src/future/future.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![stable(feature = "futures_api", since = "1.36.0")]

use crate::future::Map;
use crate::marker::Unpin;
use crate::ops;
use crate::pin::Pin;
Expand Down Expand Up @@ -103,6 +104,37 @@ pub trait Future {
#[lang = "poll"]
#[stable(feature = "futures_api", since = "1.36.0")]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;

/// Map this future's output to a different type, returning a new future of
/// the resulting type.
///
/// This function is similar to [`Option::map`] or [`Iterator::map`] where
/// it will change the type of the underlying future. This is useful to
/// chain along a computation once a future has been resolved.
///
/// Note that this function consumes the receiving future and returns a
/// wrapped version of it, similar to the existing `map` methods in the
/// standard library.
///
/// # Examples
///
/// ```
/// #![feature(future_map)]
/// use core::future::Future;
/// # async fn f() {
/// let future = async { 1 };
/// let new_future = future.map(|x| x + 3);
/// assert_eq!(new_future.await, 4);
/// # }
/// ```
#[unstable(feature = "future_map", issue = "none")]
fn map<U, F>(self, f: F) -> Map<Self, F>
where
F: FnOnce(Self::Output) -> U,
Self: Sized,
{
Map::new(self, f)
}
}

#[stable(feature = "futures_api", since = "1.36.0")]
Expand Down
37 changes: 37 additions & 0 deletions library/core/src/future/map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#![allow(unused)]

use crate::fmt;
use crate::future::Future;
use crate::pin::Pin;
use crate::task::{Context, Poll};

/// A [`Future`] that maps the output of a wrapped [`Future`].
///
/// Returned by [`Future::map`].
#[unstable(feature = "future_map", issue = "none")]
pub struct Map<Fut, F> {
future: Option<Fut>,
f: Option<F>,
}

impl<Fut, F> Map<Fut, F> {
pub(crate) fn new(future: Fut, f: F) -> Self {
Self { future: Some(future), f: Some(f) }
}
}

#[unstable(feature = "future_map", issue = "none")]
impl<Fut: fmt::Debug, F> fmt::Debug for Map<Fut, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Map").field("future", &self.future).finish()
}
}

#[unstable(feature = "future_map", issue = "none")]
impl<Fut: Future, F: FnOnce(Fut::Output) -> U, U> Future for Map<Fut, F> {
type Output = U;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Pending
}
}
4 changes: 4 additions & 0 deletions library/core/src/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::task::Context;
mod future;
mod into_future;
mod join;
mod map;
mod pending;
mod poll_fn;
mod ready;
Expand All @@ -36,6 +37,9 @@ pub use ready::{ready, Ready};
#[stable(feature = "future_poll_fn", since = "1.64.0")]
pub use poll_fn::{poll_fn, PollFn};

#[unstable(feature = "future_map", issue = "none")]
pub use map::Map;

/// This type is needed because:
///
/// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass
Expand Down
113 changes: 8 additions & 105 deletions library/core/tests/future.rs
Original file line number Diff line number Diff line change
@@ -1,105 +1,9 @@
use std::future::{join, Future};
use std::pin::Pin;
use std::future::Future;
use std::sync::Arc;
use std::task::{Context, Poll, Wake};
use std::thread;

struct PollN {
val: usize,
polled: usize,
num: usize,
}

impl Future for PollN {
type Output = usize;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.polled += 1;

if self.polled == self.num {
return Poll::Ready(self.val);
}

cx.waker().wake_by_ref();
Poll::Pending
}
}

fn poll_n(val: usize, num: usize) -> PollN {
PollN { val, num, polled: 0 }
}

#[test]
#[cfg_attr(miri, ignore)] // self-referential generators do not work with Miri's aliasing checks
fn test_join() {
block_on(async move {
let x = join!(async { 0 }).await;
assert_eq!(x, 0);

let x = join!(async { 0 }, async { 1 }).await;
assert_eq!(x, (0, 1));

let x = join!(async { 0 }, async { 1 }, async { 2 }).await;
assert_eq!(x, (0, 1, 2));

let x = join!(
poll_n(0, 1),
poll_n(1, 5),
poll_n(2, 2),
poll_n(3, 1),
poll_n(4, 2),
poll_n(5, 3),
poll_n(6, 4),
poll_n(7, 1)
)
.await;
assert_eq!(x, (0, 1, 2, 3, 4, 5, 6, 7));

let y = String::new();
let x = join!(async {
println!("{}", &y);
1
})
.await;
assert_eq!(x, 1);
});
}

/// Tests that `join!(…)` behaves "like a function": evaluating its arguments
/// before applying any of its own logic.
///
/// _e.g._, `join!(async_fn(&borrowed), …)` does not consume `borrowed`;
/// and `join!(opt_fut?, …)` does let that `?` refer to the callsite scope.
mod test_join_function_like_value_arg_semantics {
use super::*;

async fn async_fn(_: impl Sized) {}

// no need to _run_ this test, just to compile it.
fn _join_does_not_unnecessarily_move_mentioned_bindings() {
let not_copy = vec![()];
let _ = join!(async_fn(&not_copy)); // should not move `not_copy`
let _ = &not_copy; // OK
}

#[test]
fn join_lets_control_flow_effects_such_as_try_flow_through() {
let maybe_fut = None;
if false {
*&mut { maybe_fut } = Some(async {});
loop {}
}
assert!(Option::is_none(&try { join!(maybe_fut?, async { unreachable!() }) }));
}

#[test]
fn join_is_able_to_handle_temporaries() {
let _ = join!(async_fn(&String::from("temporary")));
let () = block_on(join!(async_fn(&String::from("temporary"))));
}
}

fn block_on(fut: impl Future) {
fn block_on<F: Future>(fut: F) -> F::Output {
struct Waker;
impl Wake for Waker {
fn wake(self: Arc<Self>) {
Expand All @@ -113,16 +17,15 @@ fn block_on(fut: impl Future) {

loop {
match fut.as_mut().poll(&mut cx) {
Poll::Ready(_) => break,
Poll::Ready(value) => break value,
Poll::Pending => thread::park(),
}
}
}

// just tests by whether or not this compiles
fn _pending_impl_all_auto_traits<T>() {
use std::panic::{RefUnwindSafe, UnwindSafe};
fn all_auto_traits<T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe>() {}

all_auto_traits::<std::future::Pending<T>>();
#[test]
fn test_map() {
let future = async { 1 };
let future = future.map(|x| x + 3);
assert_eq!(block_on(future), 4);
}
10 changes: 6 additions & 4 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#![feature(fmt_internals)]
#![feature(float_minimum_maximum)]
#![feature(future_join)]
#![feature(future_map)]
#![feature(generic_assert_internals)]
#![feature(array_try_from_fn)]
#![feature(hasher_prefixfree_extras)]
Expand Down Expand Up @@ -113,9 +114,9 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(fuzzy_provenance_casts)]

extern crate test;
//extern crate test;

mod alloc;
/*mod alloc;
mod any;
mod array;
mod ascii;
Expand All @@ -128,9 +129,9 @@ mod clone;
mod cmp;
mod const_ptr;
mod convert;
mod fmt;
mod fmt;*/
mod future;
mod hash;
/*mod hash;
mod intrinsics;
mod iter;
mod lazy;
Expand Down Expand Up @@ -170,3 +171,4 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng {
let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap();
rand::SeedableRng::from_seed(seed)
}
*/