Skip to content
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

Generator Resume Arguments #68524

Merged
merged 25 commits into from
Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
044fe0f
Add a resume type parameter to `Generator`
jonas-schievink Jan 25, 2020
0117033
Add a resume type param to the generator substs
jonas-schievink Jan 23, 2020
25af2f6
Use real resume type as second argument
jonas-schievink Jan 25, 2020
8a1227a
Infer type of `yield` to be resume type
jonas-schievink Jan 25, 2020
32005fe
Allow 0 or 1 explicit generator parameters
jonas-schievink Jan 25, 2020
2101a1f
Adjust tests to type inference changes
jonas-schievink Jan 25, 2020
f2c1468
Add resume arg place to `Yield` MIR terminator
jonas-schievink Jan 25, 2020
3c069a0
Change MIR building to fill in the resume place
jonas-schievink Jan 25, 2020
3c22e51
Make generator transform move resume arg around
jonas-schievink Jan 25, 2020
5b2059b
Fix error message on type mismatch in generator
jonas-schievink Jan 23, 2020
fca614e
Add tests for generator resume arguments
jonas-schievink Jan 24, 2020
4ee857c
Add test for E0628 (too many generator parameters)
jonas-schievink Jan 24, 2020
7a9709b
Fix bootstrap rustc build
jonas-schievink Jan 24, 2020
3bb8ecb
Adjust mir-opt tests to new `yield` lowering
jonas-schievink Jan 25, 2020
aae0f54
No resume argument in the drop shim
jonas-schievink Jan 27, 2020
9fa46fe
Teach dropck about resume arguments
jonas-schievink Jan 27, 2020
392e595
Fix miscompilation
jonas-schievink Feb 2, 2020
341eaf5
Add more tests for generator resume arguments
jonas-schievink Feb 4, 2020
cc66d29
Update error message with too many parameters
jonas-schievink Feb 4, 2020
72776e6
Remove obsolete test
jonas-schievink Feb 4, 2020
895aab2
Take resume argument from the right generator type
jonas-schievink Feb 4, 2020
fb66b9e
Don't emit StorageDead for the resume argument
jonas-schievink Feb 4, 2020
84dd07a
Simplify implicit resume argument
jonas-schievink Feb 6, 2020
732913a
Clarify comment about `_2` living across a yield
jonas-schievink Feb 6, 2020
9d7b214
Ignore panic-drops-resume.rs on wasm/emscripten
jonas-schievink Feb 6, 2020
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
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 @@ -2645,9 +2647,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 a generator (expected 0 or 1 parameters)"
)
.emit();
}
Expand Down
Loading