Skip to content

Commit

Permalink
Auto merge of #68524 - jonas-schievink:generator-resume-arguments, r=…
Browse files Browse the repository at this point in the history
…<try>

Generator Resume Arguments

cc #43122 and #56974
  • Loading branch information
bors committed Jan 26, 2020
2 parents 6d3f4e0 + 1738840 commit 70e8f64
Show file tree
Hide file tree
Showing 79 changed files with 657 additions and 242 deletions.
26 changes: 11 additions & 15 deletions src/doc/unstable-book/src/language-features/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ fn main() {
return "foo"
};

match Pin::new(&mut generator).resume() {
match Pin::new(&mut generator).resume(()) {
GeneratorState::Yielded(1) => {}
_ => panic!("unexpected value from resume"),
}
match Pin::new(&mut generator).resume() {
match Pin::new(&mut generator).resume(()) {
GeneratorState::Complete("foo") => {}
_ => panic!("unexpected value from resume"),
}
Expand Down Expand Up @@ -71,9 +71,9 @@ fn main() {
};

println!("1");
Pin::new(&mut generator).resume();
Pin::new(&mut generator).resume(());
println!("3");
Pin::new(&mut generator).resume();
Pin::new(&mut generator).resume(());
println!("5");
}
```
Expand All @@ -92,10 +92,10 @@ The `Generator` trait in `std::ops` currently looks like:
# use std::ops::GeneratorState;
# use std::pin::Pin;
pub trait Generator {
pub trait Generator<R = ()> {
type Yield;
type Return;
fn resume(self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return>;
fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;
}
```

Expand Down Expand Up @@ -152,10 +152,6 @@ closure-like semantics. Namely:
* Whenever a generator is dropped it will drop all captured environment
variables.

Note that unlike closures, generators at this time cannot take any arguments.
That is, generators must always look like `|| { ... }`. This restriction may be
lifted at a future date, the design is ongoing!

### Generators as state machines

In the compiler, generators are currently compiled as state machines. Each
Expand All @@ -179,8 +175,8 @@ fn main() {
return ret
};

Pin::new(&mut generator).resume();
Pin::new(&mut generator).resume();
Pin::new(&mut generator).resume(());
Pin::new(&mut generator).resume(());
}
```

Expand All @@ -205,7 +201,7 @@ fn main() {
type Yield = i32;
type Return = &'static str;

fn resume(mut self: Pin<&mut Self>) -> GeneratorState<i32, &'static str> {
fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {
use std::mem;
match mem::replace(&mut *self, __Generator::Done) {
__Generator::Start(s) => {
Expand All @@ -228,8 +224,8 @@ fn main() {
__Generator::Start(ret)
};

Pin::new(&mut generator).resume();
Pin::new(&mut generator).resume();
Pin::new(&mut generator).resume(());
Pin::new(&mut generator).resume(());
}
```

Expand Down
24 changes: 24 additions & 0 deletions src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ impl<T: ?Sized> AsMut<T> for Box<T> {
#[stable(feature = "pin", since = "1.33.0")]
impl<T: ?Sized> Unpin for Box<T> {}

#[cfg(bootstrap)]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator + Unpin> Generator for Box<G> {
type Yield = G::Yield;
Expand All @@ -1113,6 +1114,7 @@ impl<G: ?Sized + Generator + Unpin> Generator for Box<G> {
}
}

#[cfg(bootstrap)]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator> Generator for Pin<Box<G>> {
type Yield = G::Yield;
Expand All @@ -1123,6 +1125,28 @@ impl<G: ?Sized + Generator> Generator for Pin<Box<G>> {
}
}

#[cfg(not(bootstrap))]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator<R> + Unpin, R> Generator<R> for Box<G> {
type Yield = G::Yield;
type Return = G::Return;

fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return> {
G::resume(Pin::new(&mut *self), arg)
}
}

#[cfg(not(bootstrap))]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator<R>, R> Generator<R> for Pin<Box<G>> {
type Yield = G::Yield;
type Return = G::Return;

fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return> {
G::resume((*self).as_mut(), arg)
}
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<F: ?Sized + Future + Unpin> Future for Box<F> {
type Output = F::Output;
Expand Down
35 changes: 31 additions & 4 deletions src/libcore/ops/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ pub enum GeneratorState<Y, R> {
/// return "foo"
/// };
///
/// match Pin::new(&mut generator).resume() {
/// match Pin::new(&mut generator).resume(()) {
/// GeneratorState::Yielded(1) => {}
/// _ => panic!("unexpected return from resume"),
/// }
/// match Pin::new(&mut generator).resume() {
/// match Pin::new(&mut generator).resume(()) {
/// GeneratorState::Complete("foo") => {}
/// _ => panic!("unexpected return from resume"),
/// }
Expand All @@ -67,7 +67,7 @@ pub enum GeneratorState<Y, R> {
#[lang = "generator"]
#[unstable(feature = "generator_trait", issue = "43122")]
#[fundamental]
pub trait Generator {
pub trait Generator<#[cfg(not(bootstrap))] R = ()> {
/// The type of value this generator yields.
///
/// This associated type corresponds to the `yield` expression and the
Expand Down Expand Up @@ -110,9 +110,13 @@ pub trait Generator {
/// been returned previously. While generator literals in the language are
/// guaranteed to panic on resuming after `Complete`, this is not guaranteed
/// for all implementations of the `Generator` trait.
fn resume(self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return>;
fn resume(
self: Pin<&mut Self>,
#[cfg(not(bootstrap))] arg: R,
) -> GeneratorState<Self::Yield, Self::Return>;
}

#[cfg(bootstrap)]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator> Generator for Pin<&mut G> {
type Yield = G::Yield;
Expand All @@ -123,6 +127,7 @@ impl<G: ?Sized + Generator> Generator for Pin<&mut G> {
}
}

#[cfg(bootstrap)]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator + Unpin> Generator for &mut G {
type Yield = G::Yield;
Expand All @@ -132,3 +137,25 @@ impl<G: ?Sized + Generator + Unpin> Generator for &mut G {
G::resume(Pin::new(&mut *self))
}
}

#[cfg(not(bootstrap))]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator<R>, R> Generator<R> for Pin<&mut G> {
type Yield = G::Yield;
type Return = G::Return;

fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return> {
G::resume((*self).as_mut(), arg)
}
}

#[cfg(not(bootstrap))]
#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator<R> + Unpin, R> Generator<R> for &mut G {
type Yield = G::Yield;
type Return = G::Return;

fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return> {
G::resume(Pin::new(&mut *self), arg)
}
}
1 change: 1 addition & 0 deletions src/librustc/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ where

substs.as_generator().return_ty(def_id, self.tcx).visit_with(self);
substs.as_generator().yield_ty(def_id, self.tcx).visit_with(self);
substs.as_generator().resume_ty(def_id, self.tcx).visit_with(self);
}
_ => {
ty.super_visit_with(self);
Expand Down
11 changes: 8 additions & 3 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,8 @@ pub enum TerminatorKind<'tcx> {
value: Operand<'tcx>,
/// Where to resume to.
resume: BasicBlock,
/// The place to store the resume argument in.
resume_arg: Place<'tcx>,
/// Cleanup to be done if the generator is dropped at this suspend point.
drop: Option<BasicBlock>,
},
Expand Down Expand Up @@ -2641,9 +2643,12 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
target,
unwind,
},
Yield { ref value, resume, drop } => {
Yield { value: value.fold_with(folder), resume: resume, drop: drop }
}
Yield { ref value, resume, ref resume_arg, drop } => Yield {
value: value.fold_with(folder),
resume,
resume_arg: resume_arg.fold_with(folder),
drop,
},
Call { ref func, ref args, ref destination, cleanup, from_hir_call } => {
let dest =
destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest));
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,8 +516,14 @@ macro_rules! make_mir_visitor {
TerminatorKind::Yield {
value,
resume: _,
resume_arg,
drop: _,
} => {
self.visit_place(
resume_arg,
PlaceContext::MutatingUse(MutatingUseContext::Store),
source_location,
);
self.visit_operand(value, source_location);
}

Expand Down
6 changes: 4 additions & 2 deletions src/librustc/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,10 @@ pub fn generator_trait_ref_and_outputs(
self_ty: Ty<'tcx>,
sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
let trait_ref =
ty::TraitRef { def_id: fn_trait_def_id, substs: tcx.mk_substs_trait(self_ty, &[]) };
let trait_ref = ty::TraitRef {
def_id: fn_trait_def_id,
substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]),
};
ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
}

Expand Down
5 changes: 3 additions & 2 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2350,8 +2350,9 @@ impl<'tcx> ty::Instance<'tcx> {
]);
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);

tcx.mk_fn_sig(iter::once(env_ty),
ret_ty,
tcx.mk_fn_sig(
[env_ty, sig.resume_ty].iter(),
&ret_ty,
false,
hir::Unsafety::Normal,
rustc_target::spec::abi::Abi::Rust
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> {
impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> {
type Lifted = ty::GenSig<'tcx>;
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
tcx.lift(&(self.yield_ty, self.return_ty))
.map(|(yield_ty, return_ty)| ty::GenSig { yield_ty, return_ty })
tcx.lift(&(self.resume_ty, self.yield_ty, self.return_ty))
.map(|(resume_ty, yield_ty, return_ty)| ty::GenSig { resume_ty, yield_ty, return_ty })
}
}

Expand Down
41 changes: 32 additions & 9 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,17 @@ static_assert_size!(TyKind<'_>, 24);
/// ## Generators
///
/// Generators are handled similarly in `GeneratorSubsts`. The set of
/// type parameters is similar, but the role of CK and CS are
/// different. CK represents the "yield type" and CS represents the
/// "return type" of the generator.
/// type parameters is similar, but `CK` and `CS` are replaced by the
/// following type parameters:
///
/// * `GS`: The generator's "resume type", which is the type of the
/// argument passed to `resume`, and the type of `yield` expressions
/// inside the generator.
/// * `GY`: The "yield type", which is the type of values passed to
/// `yield` inside the generator.
/// * `GR`: The "return type", which is the type of value returned upon
/// completion of the generator.
/// * `GW`: The "generator witness".
#[derive(Copy, Clone, Debug, TypeFoldable)]
pub struct ClosureSubsts<'tcx> {
/// Lifetime and type parameters from the enclosing function,
Expand Down Expand Up @@ -442,6 +450,7 @@ pub struct GeneratorSubsts<'tcx> {
}

struct SplitGeneratorSubsts<'tcx> {
resume_ty: Ty<'tcx>,
yield_ty: Ty<'tcx>,
return_ty: Ty<'tcx>,
witness: Ty<'tcx>,
Expand All @@ -453,10 +462,11 @@ impl<'tcx> GeneratorSubsts<'tcx> {
let generics = tcx.generics_of(def_id);
let parent_len = generics.parent_count;
SplitGeneratorSubsts {
yield_ty: self.substs.type_at(parent_len),
return_ty: self.substs.type_at(parent_len + 1),
witness: self.substs.type_at(parent_len + 2),
upvar_kinds: &self.substs[parent_len + 3..],
resume_ty: self.substs.type_at(parent_len),
yield_ty: self.substs.type_at(parent_len + 1),
return_ty: self.substs.type_at(parent_len + 2),
witness: self.substs.type_at(parent_len + 3),
upvar_kinds: &self.substs[parent_len + 4..],
}
}

Expand Down Expand Up @@ -485,6 +495,11 @@ impl<'tcx> GeneratorSubsts<'tcx> {
})
}

/// Returns the type representing the resume type of the generator.
pub fn resume_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> {
self.split(def_id, tcx).resume_ty
}

/// Returns the type representing the yield type of the generator.
pub fn yield_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> {
self.split(def_id, tcx).yield_ty
Expand All @@ -505,10 +520,14 @@ impl<'tcx> GeneratorSubsts<'tcx> {
ty::Binder::dummy(self.sig(def_id, tcx))
}

/// Returns the "generator signature", which consists of its yield
/// Returns the "generator signature", which consists of its resume, yield
/// and return types.
pub fn sig(self, def_id: DefId, tcx: TyCtxt<'_>) -> GenSig<'tcx> {
ty::GenSig { yield_ty: self.yield_ty(def_id, tcx), return_ty: self.return_ty(def_id, tcx) }
ty::GenSig {
resume_ty: self.resume_ty(def_id, tcx),
yield_ty: self.yield_ty(def_id, tcx),
return_ty: self.return_ty(def_id, tcx),
}
}
}

Expand Down Expand Up @@ -1072,13 +1091,17 @@ impl<'tcx> ProjectionTy<'tcx> {

#[derive(Clone, Debug, TypeFoldable)]
pub struct GenSig<'tcx> {
pub resume_ty: Ty<'tcx>,
pub yield_ty: Ty<'tcx>,
pub return_ty: Ty<'tcx>,
}

pub type PolyGenSig<'tcx> = Binder<GenSig<'tcx>>;

impl<'tcx> PolyGenSig<'tcx> {
pub fn resume_ty(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|sig| sig.resume_ty)
}
pub fn yield_ty(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|sig| sig.yield_ty)
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_ast_lowering/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,12 +688,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> Option<hir::Movability> {
match generator_kind {
Some(hir::GeneratorKind::Gen) => {
if !decl.inputs.is_empty() {
if decl.inputs.len() > 1 {
struct_span_err!(
self.sess,
fn_decl_span,
E0628,
"generators cannot have explicit parameters"
"too many parameters for generator (expected 0 or 1 parameters)"
)
.emit();
}
Expand Down
Loading

0 comments on commit 70e8f64

Please sign in to comment.