Skip to content

Commit 4f27364

Browse files
committed
-Zfused-futures
1 parent 8d72d3e commit 4f27364

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

compiler/rustc_abi/src/layout/ty.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ rustc_index::newtype_index! {
6565
const FIRST_VARIANT = 0;
6666
}
6767
}
68+
69+
impl VariantIdx {
70+
/// The second variant, at index 1.
71+
///
72+
/// For use alongside [`VariantIdx::ZERO`].
73+
pub const ONE: VariantIdx = VariantIdx::from_u32(1);
74+
}
75+
6876
#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
6977
#[rustc_pass_by_value]
7078
pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
//! For coroutines with state 1 (returned) and state 2 (poisoned) it panics.
4646
//! Otherwise it continues the execution from the last suspension point.
4747
//!
48+
//! If -Zfused-futures is given however, then `Future::poll` from the state 1 (returned)
49+
//! will not panic and will instead return `Poll::Pending`.
50+
//!
4851
//! The other function is the drop glue for the coroutine.
4952
//! For coroutines with state 0 (unresumed) it drops the upvars of the coroutine.
5053
//! For coroutines with state 1 (returned) and state 2 (poisoned) it does nothing.
@@ -218,10 +221,17 @@ impl<'tcx> TransformVisitor<'tcx> {
218221
let source_info = SourceInfo::outermost(body.span);
219222

220223
let none_value = match self.coroutine_kind {
224+
CoroutineKind::Coroutine(_) => span_bug!(body.span, "`Coroutine`s cannot be fused"),
225+
// Fused futures continue to return `Poll::Pending`.
221226
CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
222-
span_bug!(body.span, "`Future`s are not fused inherently")
227+
let poll_def_id = self.tcx.require_lang_item(LangItem::Poll, body.span);
228+
make_aggregate_adt(
229+
poll_def_id,
230+
VariantIdx::ONE,
231+
self.tcx.mk_args(&[self.old_yield_ty.into()]),
232+
IndexVec::new(),
233+
)
223234
}
224-
CoroutineKind::Coroutine(_) => span_bug!(body.span, "`Coroutine`s cannot be fused"),
225235
// `gen` continues return `None`
226236
CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
227237
let option_def_id = self.tcx.require_lang_item(LangItem::Option, body.span);
@@ -278,7 +288,7 @@ impl<'tcx> TransformVisitor<'tcx> {
278288
statements: &mut Vec<Statement<'tcx>>,
279289
) {
280290
const ZERO: VariantIdx = VariantIdx::ZERO;
281-
const ONE: VariantIdx = VariantIdx::from_usize(1);
291+
const ONE: VariantIdx = VariantIdx::ONE;
282292
let rvalue = match self.coroutine_kind {
283293
CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
284294
let poll_def_id = self.tcx.require_lang_item(LangItem::Poll, source_info.span);
@@ -1099,7 +1109,7 @@ fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) ->
10991109
const_: Const::zero_sized(tcx.types.unit),
11001110
}));
11011111
let ready_val = Rvalue::Aggregate(
1102-
Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::from_usize(0), args, None, None)),
1112+
Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::ZERO, args, None, None)),
11031113
IndexVec::from_raw(vec![val]),
11041114
);
11051115
Statement::new(source_info, StatementKind::Assign(Box::new((Place::return_place(), ready_val))))
@@ -1252,13 +1262,18 @@ fn create_coroutine_resume_function<'tcx>(
12521262
}
12531263

12541264
if can_return {
1265+
// TODO: simplify this match
12551266
let block = match transform.coroutine_kind {
12561267
CoroutineKind::Desugared(CoroutineDesugaring::Async, _)
12571268
| CoroutineKind::Coroutine(_) => {
12581269
// For `async_drop_in_place<T>::{closure}` we just keep return Poll::Ready,
12591270
// because async drop of such coroutine keeps polling original coroutine
12601271
if tcx.is_async_drop_in_place_coroutine(body.source.def_id()) {
12611272
insert_poll_ready_block(tcx, body)
1273+
} else if tcx.sess.opts.unstable_opts.fused_futures
1274+
&& !matches!(transform.coroutine_kind, CoroutineKind::Coroutine(_))
1275+
{
1276+
transform.insert_none_ret_block(body)
12621277
} else {
12631278
insert_panic_block(tcx, body, ResumedAfterReturn(transform.coroutine_kind))
12641279
}

compiler/rustc_session/src/options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,6 +2336,8 @@ options! {
23362336
"replace returns with jumps to `__x86_return_thunk` (default: `keep`)"),
23372337
function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED],
23382338
"whether each function should go in its own section"),
2339+
fused_futures: bool = (false, parse_bool, [TRACKED],
2340+
"make compiler-generated futures return `Poll::Pending` and not `panic!` when polled after completion"),
23392341
future_incompat_test: bool = (false, parse_bool, [UNTRACKED],
23402342
"forces all lints to be future incompatible, used for internal testing (default: no)"),
23412343
graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED],
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ run-pass
2+
//@ edition: 2024
3+
//@ compile-flags: -Zfused-futures
4+
5+
use std::pin::pin;
6+
use std::task::{Context, Poll, Waker};
7+
8+
async fn foo() {}
9+
10+
const N: usize = 10;
11+
12+
fn main() {
13+
let cx = &mut Context::from_waker(Waker::noop());
14+
let mut x = pin!(foo());
15+
assert_eq!(x.as_mut().poll(cx), Poll::Ready(()));
16+
for _ in 0..N {
17+
assert_eq!(x.as_mut().poll(cx), Poll::Pending);
18+
}
19+
}

0 commit comments

Comments
 (0)