Rollup merge of #68524 - jonas-schievink:generator-resume-arguments, r=Zoxc

Generator Resume Arguments

cc https://github.com/rust-lang/rust/issues/43122 and https://github.com/rust-lang/rust/issues/56974

Blockers:
* [x] Fix miscompilation when resume argument is live across a yield point (https://github.com/rust-lang/rust/pull/68524#issuecomment-578459069)
* [x] Fix 10% compile time regression in `await-call-tree` benchmarks (https://github.com/rust-lang/rust/pull/68524#issuecomment-578487162)
  * [x] Fix remaining 1-3% regression (https://github.com/rust-lang/rust/pull/68524#issuecomment-579566255) - resolved (https://github.com/rust-lang/rust/pull/68524#issuecomment-581144901)
* [x] Make dropck rules account for resume arguments (https://github.com/rust-lang/rust/pull/68524#issuecomment-578541137)

Follow-up work:
* Change async/await desugaring to make use of this feature
* Rewrite [`box_region.rs`](3d8778d767/src/librustc_data_structures/box_region.rs) to use resume arguments (this shows up in profiles too)
This commit is contained in:
Dylan DPC 2020-02-06 22:38:33 +01:00 committed by GitHub
commit 2d8f6389d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 867 additions and 283 deletions

View File

@ -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"),
}
@ -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");
}
```
@ -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>;
}
```
@ -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
@ -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(());
}
```
@ -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) => {
@ -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(());
}
```

View File

@ -1104,6 +1104,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;
@ -1114,6 +1115,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;
@ -1124,6 +1126,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;

View File

@ -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"),
/// }
@ -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
@ -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;
@ -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;
@ -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)
}
}

View File

@ -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);

View File

@ -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>,
},
@ -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));

View File

@ -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);
}

View File

@ -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))
}

View File

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

View File

@ -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 })
}
}

View File

@ -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,
@ -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>,
@ -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..],
}
}
@ -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
@ -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),
}
}
}
@ -1072,6 +1091,7 @@ 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>,
}
@ -1079,6 +1099,9 @@ pub struct GenSig<'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)
}

View File

@ -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();
}

View File

@ -25,6 +25,7 @@ pub struct PinnedGenerator<I, A, R> {
}
impl<I, A, R> PinnedGenerator<I, A, R> {
#[cfg(bootstrap)]
pub fn new<T: Generator<Yield = YieldType<I, A>, Return = R> + 'static>(
generator: T,
) -> (I, Self) {
@ -39,6 +40,22 @@ impl<I, A, R> PinnedGenerator<I, A, R> {
(init, result)
}
#[cfg(not(bootstrap))]
pub fn new<T: Generator<Yield = YieldType<I, A>, Return = R> + 'static>(
generator: T,
) -> (I, Self) {
let mut result = PinnedGenerator { generator: Box::pin(generator) };
// Run it to the first yield to set it up
let init = match Pin::new(&mut result.generator).resume(()) {
GeneratorState::Yielded(YieldType::Initial(y)) => y,
_ => panic!(),
};
(init, result)
}
#[cfg(bootstrap)]
pub unsafe fn access(&mut self, closure: *mut dyn FnMut()) {
BOX_REGION_ARG.with(|i| {
i.set(Action::Access(AccessAction(closure)));
@ -50,6 +67,19 @@ impl<I, A, R> PinnedGenerator<I, A, R> {
}
}
#[cfg(not(bootstrap))]
pub unsafe fn access(&mut self, closure: *mut dyn FnMut()) {
BOX_REGION_ARG.with(|i| {
i.set(Action::Access(AccessAction(closure)));
});
// Call the generator, which in turn will call the closure in BOX_REGION_ARG
if let GeneratorState::Complete(_) = Pin::new(&mut self.generator).resume(()) {
panic!()
}
}
#[cfg(bootstrap)]
pub fn complete(&mut self) -> R {
// Tell the generator we want it to complete, consuming it and yielding a result
BOX_REGION_ARG.with(|i| i.set(Action::Complete));
@ -57,6 +87,15 @@ impl<I, A, R> PinnedGenerator<I, A, R> {
let result = Pin::new(&mut self.generator).resume();
if let GeneratorState::Complete(r) = result { r } else { panic!() }
}
#[cfg(not(bootstrap))]
pub fn complete(&mut self) -> R {
// Tell the generator we want it to complete, consuming it and yielding a result
BOX_REGION_ARG.with(|i| i.set(Action::Complete));
let result = Pin::new(&mut self.generator).resume(());
if let GeneratorState::Complete(r) = result { r } else { panic!() }
}
}
#[derive(PartialEq)]

View File

@ -12,7 +12,7 @@ let mut b = || {
yield (); // ...is still in scope here, when the yield occurs.
println!("{}", a);
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
```
At present, it is not permitted to have a yield that occurs while a
@ -31,7 +31,7 @@ let mut b = || {
yield ();
println!("{}", a);
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
```
This is a very simple case, of course. In more complex cases, we may
@ -50,7 +50,7 @@ let mut b = || {
yield x; // ...when this yield occurs.
}
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
```
Such cases can sometimes be resolved by iterating "by value" (or using
@ -66,7 +66,7 @@ let mut b = || {
yield x; // <-- Now yield is OK.
}
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
```
If taking ownership is not an option, using indices can work too:
@ -83,7 +83,7 @@ let mut b = || {
yield x; // <-- Now yield is OK.
}
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
// (*) -- Unfortunately, these temporaries are currently required.
// See <https://github.com/rust-lang/rust/issues/43122>.

View File

@ -159,7 +159,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
self.consume_operand(location, index);
}
}
TerminatorKind::Yield { ref value, resume, drop: _ } => {
TerminatorKind::Yield { ref value, resume, resume_arg, drop: _ } => {
self.consume_operand(location, value);
// Invalidate all borrows of local places
@ -170,6 +170,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
self.all_facts.invalidates.push((resume, i));
}
}
self.mutate_place(location, resume_arg, Deep, JustWrite);
}
TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
// Invalidate all borrows of local places

View File

@ -684,7 +684,7 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
}
}
TerminatorKind::Yield { ref value, resume: _, drop: _ } => {
TerminatorKind::Yield { ref value, resume: _, ref resume_arg, drop: _ } => {
self.consume_operand(loc, (value, span), flow_state);
if self.movable_generator {
@ -697,6 +697,8 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
}
});
}
self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state);
}
TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {

View File

@ -581,9 +581,11 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
DefiningTy::Generator(def_id, substs, movability) => {
assert_eq!(self.mir_def_id, def_id);
let resume_ty = substs.as_generator().resume_ty(def_id, tcx);
let output = substs.as_generator().return_ty(def_id, tcx);
let generator_ty = tcx.mk_generator(def_id, substs, movability);
let inputs_and_output = self.infcx.tcx.intern_type_list(&[generator_ty, output]);
let inputs_and_output =
self.infcx.tcx.intern_type_list(&[generator_ty, resume_ty, output]);
ty::Binder::dummy(inputs_and_output)
}

View File

@ -31,10 +31,12 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
self.body.local_decls.len()
}
fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
// Nothing is live on function entry (generators only have a self
// argument, and we don't care about that)
assert_eq!(1, self.body.arg_count);
fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
// The resume argument is live on function entry (we don't care about
// the `self` argument)
for arg in self.body.args_iter().skip(1) {
on_entry.insert(arg);
}
}
fn statement_effect(&self, trans: &mut GenKillSet<Local>, loc: Location) {
@ -100,10 +102,12 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
self.body.local_decls.len()
}
fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
// Nothing is live on function entry (generators only have a self
// argument, and we don't care about that)
assert_eq!(1, self.body.arg_count);
fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
// The resume argument is live on function entry (we don't care about
// the `self` argument)
for arg in self.body.args_iter().skip(1) {
on_entry.insert(arg);
}
}
fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {

View File

@ -380,7 +380,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
self.gather_operand(discr);
}
TerminatorKind::Yield { ref value, .. } => {
TerminatorKind::Yield { ref value, resume_arg: ref place, .. } => {
self.create_move_path(place);
self.gather_init(place.as_ref(), InitKind::Deep);
self.gather_operand(value);
}

View File

@ -192,9 +192,10 @@ const RETURNED: usize = GeneratorSubsts::RETURNED;
/// Generator has been poisoned
const POISONED: usize = GeneratorSubsts::POISONED;
struct SuspensionPoint {
struct SuspensionPoint<'tcx> {
state: usize,
resume: BasicBlock,
resume_arg: Place<'tcx>,
drop: Option<BasicBlock>,
storage_liveness: liveness::LiveVarSet,
}
@ -216,7 +217,7 @@ struct TransformVisitor<'tcx> {
storage_liveness: FxHashMap<BasicBlock, liveness::LiveVarSet>,
// A list of suspension points, generated during the transform
suspension_points: Vec<SuspensionPoint>,
suspension_points: Vec<SuspensionPoint<'tcx>>,
// The original RETURN_PLACE local
new_ret_local: Local,
@ -303,8 +304,8 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
Operand::Move(Place::from(self.new_ret_local)),
None,
)),
TerminatorKind::Yield { ref value, resume, drop } => {
Some((VariantIdx::new(0), Some(resume), value.clone(), drop))
TerminatorKind::Yield { ref value, resume, resume_arg, drop } => {
Some((VariantIdx::new(0), Some((resume, resume_arg)), value.clone(), drop))
}
_ => None,
};
@ -319,13 +320,14 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
self.make_state(state_idx, v),
)),
});
let state = if let Some(resume) = resume {
let state = if let Some((resume, resume_arg)) = resume {
// Yield
let state = 3 + self.suspension_points.len();
self.suspension_points.push(SuspensionPoint {
state,
resume,
resume_arg,
drop,
storage_liveness: self.storage_liveness.get(&block).unwrap().clone(),
});
@ -378,28 +380,35 @@ fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
PinArgVisitor { ref_gen_ty, tcx }.visit_body(body);
}
fn replace_result_variable<'tcx>(
ret_ty: Ty<'tcx>,
/// Allocates a new local and replaces all references of `local` with it. Returns the new local.
///
/// `local` will be changed to a new local decl with type `ty`.
///
/// Note that the new local will be uninitialized. It is the caller's responsibility to assign some
/// valid value to it before its first use.
fn replace_local<'tcx>(
local: Local,
ty: Ty<'tcx>,
body: &mut BodyAndCache<'tcx>,
tcx: TyCtxt<'tcx>,
) -> Local {
let source_info = source_info(body);
let new_ret = LocalDecl {
let new_decl = LocalDecl {
mutability: Mutability::Mut,
ty: ret_ty,
ty,
user_ty: UserTypeProjections::none(),
source_info,
internal: false,
is_block_tail: None,
local_info: LocalInfo::Other,
};
let new_ret_local = Local::new(body.local_decls.len());
body.local_decls.push(new_ret);
body.local_decls.swap(RETURN_PLACE, new_ret_local);
let new_local = Local::new(body.local_decls.len());
body.local_decls.push(new_decl);
body.local_decls.swap(local, new_local);
RenameLocalVisitor { from: RETURN_PLACE, to: new_ret_local, tcx }.visit_body(body);
RenameLocalVisitor { from: local, to: new_local, tcx }.visit_body(body);
new_ret_local
new_local
}
struct StorageIgnored(liveness::LiveVarSet);
@ -792,6 +801,10 @@ fn compute_layout<'tcx>(
(remap, layout, storage_liveness)
}
/// Replaces the entry point of `body` with a block that switches on the generator discriminant and
/// dispatches to blocks according to `cases`.
///
/// After this function, the former entry point of the function will be bb1.
fn insert_switch<'tcx>(
body: &mut BodyAndCache<'tcx>,
cases: Vec<(usize, BasicBlock)>,
@ -885,10 +898,11 @@ fn create_generator_drop_shim<'tcx>(
drop_clean: BasicBlock,
) -> BodyAndCache<'tcx> {
let mut body = body.clone();
body.arg_count = 1; // make sure the resume argument is not included here
let source_info = source_info(&body);
let mut cases = create_cases(&mut body, transform, |point| point.drop);
let mut cases = create_cases(&mut body, transform, Operation::Drop);
cases.insert(0, (UNRESUMED, drop_clean));
@ -1006,7 +1020,7 @@ fn create_generator_resume_function<'tcx>(
}
}
let mut cases = create_cases(body, &transform, |point| Some(point.resume));
let mut cases = create_cases(body, &transform, Operation::Resume);
use rustc::mir::interpret::PanicInfo::{ResumedAfterPanic, ResumedAfterReturn};
@ -1056,14 +1070,27 @@ fn insert_clean_drop(body: &mut BodyAndCache<'_>) -> BasicBlock {
drop_clean
}
fn create_cases<'tcx, F>(
/// An operation that can be performed on a generator.
#[derive(PartialEq, Copy, Clone)]
enum Operation {
Resume,
Drop,
}
impl Operation {
fn target_block(self, point: &SuspensionPoint<'_>) -> Option<BasicBlock> {
match self {
Operation::Resume => Some(point.resume),
Operation::Drop => point.drop,
}
}
}
fn create_cases<'tcx>(
body: &mut BodyAndCache<'tcx>,
transform: &TransformVisitor<'tcx>,
target: F,
) -> Vec<(usize, BasicBlock)>
where
F: Fn(&SuspensionPoint) -> Option<BasicBlock>,
{
operation: Operation,
) -> Vec<(usize, BasicBlock)> {
let source_info = source_info(body);
transform
@ -1071,12 +1098,19 @@ where
.iter()
.filter_map(|point| {
// Find the target for this suspension point, if applicable
target(point).map(|target| {
operation.target_block(point).map(|target| {
let block = BasicBlock::new(body.basic_blocks().len());
let mut statements = Vec::new();
// Create StorageLive instructions for locals with live storage
for i in 0..(body.local_decls.len()) {
if i == 2 {
// The resume argument is live on function entry. Don't insert a
// `StorageLive`, or the following `Assign` will read from uninitialized
// memory.
continue;
}
let l = Local::new(i);
if point.storage_liveness.contains(l) && !transform.remap.contains_key(&l) {
statements
@ -1084,6 +1118,18 @@ where
}
}
if operation == Operation::Resume {
// Move the resume argument to the destination place of the `Yield` terminator
let resume_arg = Local::new(2); // 0 = return, 1 = self
statements.push(Statement {
source_info,
kind: StatementKind::Assign(box (
point.resume_arg,
Rvalue::Use(Operand::Move(resume_arg.into())),
)),
});
}
// Then jump to the real target
body.basic_blocks_mut().push(BasicBlockData {
statements,
@ -1138,7 +1184,29 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
// We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
// RETURN_PLACE then is a fresh unused local with type ret_ty.
let new_ret_local = replace_result_variable(ret_ty, body, tcx);
let new_ret_local = replace_local(RETURN_PLACE, ret_ty, body, tcx);
// We also replace the resume argument and insert an `Assign`.
// This is needed because the resume argument `_2` might be live across a `yield`, in which
// case there is no `Assign` to it that the transform can turn into a store to the generator
// state. After the yield the slot in the generator state would then be uninitialized.
let resume_local = Local::new(2);
let new_resume_local =
replace_local(resume_local, body.local_decls[resume_local].ty, body, tcx);
// When first entering the generator, move the resume argument into its new local.
let source_info = source_info(body);
let stmts = &mut body.basic_blocks_mut()[BasicBlock::new(0)].statements;
stmts.insert(
0,
Statement {
source_info,
kind: StatementKind::Assign(box (
new_resume_local.into(),
Rvalue::Use(Operand::Move(resume_local.into())),
)),
},
);
// Extract locals which are live across suspension point into `layout`
// `remap` gives a mapping from local indices onto generator struct indices
@ -1162,9 +1230,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
};
transform.visit_body(body);
// Update our MIR struct to reflect the changed we've made
// Update our MIR struct to reflect the changes we've made
body.yield_ty = None;
body.arg_count = 1;
body.arg_count = 2; // self, resume arg
body.spread_arg = None;
body.generator_layout = Some(layout);

View File

@ -230,18 +230,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = unpack!(this.stmt_expr(block, expr, None));
block.and(this.unit_rvalue())
}
ExprKind::Yield { value } => {
let value = unpack!(block = this.as_operand(block, scope, value));
let resume = this.cfg.start_new_block();
let cleanup = this.generator_drop_cleanup();
this.cfg.terminate(
block,
source_info,
TerminatorKind::Yield { value: value, resume: resume, drop: cleanup },
);
resume.and(this.unit_rvalue())
}
ExprKind::Literal { .. }
ExprKind::Yield { .. }
| ExprKind::Literal { .. }
| ExprKind::StaticRef { .. }
| ExprKind::Block { .. }
| ExprKind::Match { .. }

View File

@ -50,6 +50,7 @@ impl Category {
| ExprKind::Adt { .. }
| ExprKind::Borrow { .. }
| ExprKind::AddressOf { .. }
| ExprKind::Yield { .. }
| ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Array { .. }
@ -63,7 +64,6 @@ impl Category {
| ExprKind::Repeat { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Yield { .. }
| ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => Some(Category::Constant),

View File

@ -365,6 +365,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.unit()
}
ExprKind::Yield { value } => {
let scope = this.local_scope();
let value = unpack!(block = this.as_operand(block, scope, value));
let resume = this.cfg.start_new_block();
let cleanup = this.generator_drop_cleanup();
this.cfg.terminate(
block,
source_info,
TerminatorKind::Yield {
value,
resume,
resume_arg: destination.clone(),
drop: cleanup,
},
);
resume.unit()
}
// these are the cases that are more naturally handled by some other mode
ExprKind::Unary { .. }
| ExprKind::Binary { .. }
@ -376,8 +394,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Tuple { .. }
| ExprKind::Closure { .. }
| ExprKind::Literal { .. }
| ExprKind::StaticRef { .. }
| ExprKind::Yield { .. } => {
| ExprKind::StaticRef { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
// should be handled above
Category::Rvalue(RvalueFunc::Into) => false,

View File

@ -68,6 +68,12 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> {
let fn_sig = cx.tables().liberated_fn_sigs()[id];
let fn_def_id = tcx.hir().local_def_id(id);
let safety = match fn_sig.unsafety {
hir::Unsafety::Normal => Safety::Safe,
hir::Unsafety::Unsafe => Safety::FnUnsafe,
};
let body = tcx.hir().body(body_id);
let ty = tcx.type_of(fn_def_id);
let mut abi = fn_sig.abi;
let implicit_argument = match ty.kind {
@ -75,21 +81,25 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> {
// HACK(eddyb) Avoid having RustCall on closures,
// as it adds unnecessary (and wrong) auto-tupling.
abi = Abi::Rust;
Some(ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None))
vec![ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None)]
}
ty::Generator(..) => {
let gen_ty = tcx.body_tables(body_id).node_type(id);
Some(ArgInfo(gen_ty, None, None, None))
// The resume argument may be missing, in that case we need to provide it here.
// It will always be `()` in this case.
if body.params.is_empty() {
vec![
ArgInfo(gen_ty, None, None, None),
ArgInfo(tcx.mk_unit(), None, None, None),
]
} else {
vec![ArgInfo(gen_ty, None, None, None)]
}
}
_ => None,
_ => vec![],
};
let safety = match fn_sig.unsafety {
hir::Unsafety::Normal => Safety::Safe,
hir::Unsafety::Unsafe => Safety::FnUnsafe,
};
let body = tcx.hir().body(body_id);
let explicit_arguments = body.params.iter().enumerate().map(|(index, arg)| {
let owner_id = tcx.hir().body_owner(body_id);
let opt_ty_info;

View File

@ -227,8 +227,8 @@ fn dtorck_constraint_for_ty<'tcx>(
// In particular, skipping over `_interior` is safe
// because any side-effects from dropping `_interior` can
// only take place through references with lifetimes
// derived from lifetimes attached to the upvars, and we
// *do* incorporate the upvars here.
// derived from lifetimes attached to the upvars and resume
// argument, and we *do* incorporate those here.
constraints.outlives.extend(
substs
@ -236,6 +236,7 @@ fn dtorck_constraint_for_ty<'tcx>(
.upvar_tys(def_id, tcx)
.map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }),
);
constraints.outlives.push(substs.as_generator().resume_ty(def_id, tcx).into());
}
ty::Adt(def, substs) => {

View File

@ -92,8 +92,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.into(),
GenericParamDefKind::Const => span_bug!(expr.span, "closure has const param"),
});
if let Some(GeneratorTypes { yield_ty, interior, movability }) = generator_types {
if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types
{
let generator_substs = substs.as_generator();
self.demand_eqtype(
expr.span,
resume_ty,
generator_substs.resume_ty(expr_def_id, self.tcx),
);
self.demand_eqtype(
expr.span,
yield_ty,
@ -259,8 +265,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => return None,
}
} else {
// Generators cannot have explicit arguments.
vec![]
// Generators with a `()` resume type may be defined with 0 or 1 explicit arguments,
// else they must have exactly 1 argument. For now though, just give up in this case.
return None;
};
let ret_param_ty = projection.skip_binder().ty;

View File

@ -1796,9 +1796,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
src: &'tcx hir::YieldSource,
) -> Ty<'tcx> {
match self.yield_ty {
Some(ty) => {
self.check_expr_coercable_to_type(&value, ty);
match self.resume_yield_tys {
Some((resume_ty, yield_ty)) => {
self.check_expr_coercable_to_type(&value, yield_ty);
resume_ty
}
// Given that this `yield` expression was generated as a result of lowering a `.await`,
// we know that the yield type must be `()`; however, the context won't contain this
@ -1806,6 +1808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// value's type against `()` (this check should always hold).
None if src == &hir::YieldSource::Await => {
self.check_expr_coercable_to_type(&value, self.tcx.mk_unit());
self.tcx.mk_unit()
}
_ => {
struct_span_err!(
@ -1815,9 +1818,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"yield expression outside of generator literal"
)
.emit();
self.tcx.mk_unit()
}
}
self.tcx.mk_unit()
}
}

View File

@ -573,7 +573,7 @@ pub struct FnCtxt<'a, 'tcx> {
/// First span of a return site that we find. Used in error messages.
ret_coercion_span: RefCell<Option<Span>>,
yield_ty: Option<Ty<'tcx>>,
resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
ps: RefCell<UnsafetyState>,
@ -1251,6 +1251,9 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
/// includes yield), it returns back some information about the yield
/// points.
struct GeneratorTypes<'tcx> {
/// Type of generator argument / values returned by `yield`.
resume_ty: Ty<'tcx>,
/// Type of value that is yielded.
yield_ty: Ty<'tcx>,
@ -1311,7 +1314,11 @@ fn check_fn<'a, 'tcx>(
let yield_ty = fcx
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
fcx.yield_ty = Some(yield_ty);
// Resume type defaults to `()` if the generator has no argument.
let resume_ty = fn_sig.inputs().get(0).map(|ty| *ty).unwrap_or_else(|| tcx.mk_unit());
fcx.resume_yield_tys = Some((resume_ty, yield_ty));
}
let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id));
@ -1364,8 +1371,11 @@ fn check_fn<'a, 'tcx>(
let interior = fcx
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
Some(GeneratorTypes {
yield_ty: fcx.yield_ty.unwrap(),
resume_ty,
yield_ty,
interior,
movability: can_be_generator.unwrap(),
})
@ -2767,7 +2777,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err_count_on_creation: inh.tcx.sess.err_count(),
ret_coercion: None,
ret_coercion_span: RefCell::new(None),
yield_ty: None,
resume_yield_tys: None,
ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
diverges: Cell::new(Diverges::Maybe),
has_errors: Cell::new(false),

View File

@ -1189,7 +1189,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
// and we don't do that for closures.
if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., gen), .. }) = node {
let dummy_args = if gen.is_some() {
&["<yield_ty>", "<return_ty>", "<witness>"][..]
&["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>"][..]
} else {
&["<closure_kind>", "<closure_signature>"][..]
};

View File

@ -40,7 +40,10 @@ impl<T: Generator<Yield = ()>> Future for GenFuture<T> {
// Safe because we're !Unpin + !Drop mapping to a ?Unpin value
let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };
let _guard = unsafe { set_task_context(cx) };
match gen.resume() {
match gen.resume(
#[cfg(not(bootstrap))]
(),
) {
GeneratorState::Yielded(()) => Poll::Pending,
GeneratorState::Complete(x) => Poll::Ready(x),
}

View File

@ -78,9 +78,9 @@ fn main() {
_zzz(); // #break
a = c;
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume();
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
Pin::new(&mut b).resume(());
Pin::new(&mut b).resume(());
_zzz(); // #break
}

View File

@ -57,11 +57,11 @@ fn main() {
println!("{} {} {}", a, c, d);
};
_zzz(); // #break
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
_zzz(); // #break
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
_zzz(); // #break
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
_zzz(); // #break
}

View File

@ -45,7 +45,7 @@ fn main() {
yield;
};
let mut b = move || {
Pin::new(&mut a).resume();
Pin::new(&mut a).resume(());
yield;
};

View File

@ -13,12 +13,12 @@ fn main() {
// START rustc.main-{{closure}}.generator_drop.0.mir
// bb0: {
// _5 = discriminant((*_1));
// switchInt(move _5) -> [0u32: bb4, 3u32: bb7, otherwise: bb8];
// _7 = discriminant((*_1));
// switchInt(move _7) -> [0u32: bb4, 3u32: bb7, otherwise: bb8];
// }
// bb1: {
// StorageDead(_4);
// StorageDead(_3);
// StorageDead(_2);
// goto -> bb5;
// }
// bb2: {
@ -37,8 +37,8 @@ fn main() {
// goto -> bb3;
// }
// bb7: {
// StorageLive(_2);
// StorageLive(_3);
// StorageLive(_4);
// goto -> bb1;
// }
// bb8: {

View File

@ -31,81 +31,81 @@ fn main() {
// START rustc.main-{{closure}}.StateTransform.before.mir
// ...
// let _2: Foo;
// let _3: Foo;
// ...
// let mut _7: Foo;
// let mut _8: Foo;
// ...
// let mut _9: Bar;
// let mut _10: Bar;
// scope 1 {
// debug a => _2;
// let _3: Bar;
// debug a => _3;
// let _4: Bar;
// scope 2 {
// debug b => _3;
// debug b => _4;
// }
// }
// bb0: {
// StorageLive(_2);
// _2 = Foo(const 5i32,);
// StorageLive(_3);
// _3 = Bar(const 6i32,);
// _3 = Foo(const 5i32,);
// StorageLive(_4);
// _4 = Bar(const 6i32,);
// ...
// _1 = suspend(move _5) -> [resume: bb2, drop: bb4];
// _1 = suspend(move _6) -> [resume: bb2, drop: bb4];
// }
// bb1 (cleanup): {
// resume;
// }
// bb2: {
// ...
// StorageLive(_6);
// StorageLive(_7);
// _7 = move _2;
// _6 = const take::<Foo>(move _7) -> [return: bb7, unwind: bb9];
// StorageLive(_8);
// _8 = move _3;
// _7 = const take::<Foo>(move _8) -> [return: bb7, unwind: bb9];
// }
// bb3 (cleanup): {
// StorageDead(_2);
// StorageDead(_3);
// drop(_1) -> bb1;
// }
// bb4: {
// ...
// StorageDead(_3);
// drop(_2) -> [return: bb5, unwind: bb3];
// StorageDead(_4);
// drop(_3) -> [return: bb5, unwind: bb3];
// }
// bb5: {
// StorageDead(_2);
// StorageDead(_3);
// drop(_1) -> [return: bb6, unwind: bb1];
// }
// bb6: {
// generator_drop;
// }
// bb7: {
// StorageDead(_8);
// StorageDead(_7);
// StorageDead(_6);
// StorageLive(_8);
// StorageLive(_9);
// _9 = move _3;
// _8 = const take::<Bar>(move _9) -> [return: bb10, unwind: bb11];
// StorageLive(_10);
// _10 = move _4;
// _9 = const take::<Bar>(move _10) -> [return: bb10, unwind: bb11];
// }
// bb8 (cleanup): {
// StorageDead(_4);
// StorageDead(_3);
// StorageDead(_2);
// drop(_1) -> bb1;
// }
// bb9 (cleanup): {
// StorageDead(_8);
// StorageDead(_7);
// StorageDead(_6);
// goto -> bb8;
// }
// bb10: {
// StorageDead(_10);
// StorageDead(_9);
// StorageDead(_8);
// ...
// StorageDead(_4);
// StorageDead(_3);
// StorageDead(_2);
// drop(_1) -> [return: bb12, unwind: bb1];
// }
// bb11 (cleanup): {
// StorageDead(_10);
// StorageDead(_9);
// StorageDead(_8);
// goto -> bb8;
// }
// bb12: {

View File

@ -16,7 +16,7 @@ fn main() {
yield;
};
panic::catch_unwind(panic::AssertUnwindSafe(|| {
let x = Pin::new(&mut g).resume();
let x = Pin::new(&mut g).resume(());
}));
Pin::new(&mut g).resume();
Pin::new(&mut g).resume(());
}

View File

@ -19,7 +19,7 @@ fn main() {
let mut g = || {
yield;
};
Pin::new(&mut g).resume(); // Yields once.
Pin::new(&mut g).resume(); // Completes here.
Pin::new(&mut g).resume(); // Panics here.
Pin::new(&mut g).resume(()); // Yields once.
Pin::new(&mut g).resume(()); // Completes here.
Pin::new(&mut g).resume(()); // Panics here.
}

View File

@ -184,7 +184,7 @@ fn generator(a: &Allocator, run_count: usize) {
);
};
for _ in 0..run_count {
Pin::new(&mut gen).resume();
Pin::new(&mut gen).resume(());
}
}

View File

@ -13,7 +13,7 @@ pub fn want_cyclic_generator_return<T>(_: T)
fn supply_cyclic_generator_return() {
want_cyclic_generator_return(|| {
//~^ ERROR closure/generator type that references itself
//~^ ERROR type mismatch
if false { yield None.unwrap(); }
None.unwrap()
})

View File

@ -1,13 +1,13 @@
error[E0644]: closure/generator type that references itself
--> $DIR/generator-yielding-or-returning-itself.rs:15:34
error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _] as std::ops::Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _]`
--> $DIR/generator-yielding-or-returning-itself.rs:15:5
|
LL | want_cyclic_generator_return(|| {
| __________________________________^
LL | |
LL | | if false { yield None.unwrap(); }
LL | | None.unwrap()
LL | | })
| |_____^ cyclic type of infinite size
LL | pub fn want_cyclic_generator_return<T>(_: T)
| ----------------------------
LL | where T: Generator<Yield = (), Return = T>
| ---------- required by this bound in `want_cyclic_generator_return`
...
LL | want_cyclic_generator_return(|| {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
|
= note: closures cannot capture themselves or take themselves as argument;
this error may be the result of a recent compiler bug-fix,
@ -30,5 +30,4 @@ LL | want_cyclic_generator_yield(|| {
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0644.
For more information about an error, try `rustc --explain E0271`.
For more information about this error, try `rustc --explain E0271`.

View File

@ -6,7 +6,7 @@ fn msg() -> u32 {
0
}
pub fn foo() -> impl Generator<Yield=(), Return=u32> {
pub fn foo() -> impl Generator<(), Yield=(), Return=u32> {
|| {
yield;
return msg();

View File

@ -3,7 +3,7 @@
use std::marker::Unpin;
use std::ops::Generator;
pub fn foo() -> impl Generator<Yield = (), Return = ()> {
pub fn foo() -> impl Generator<(), Yield = (), Return = ()> {
|| {
if false {
yield;
@ -11,7 +11,7 @@ pub fn foo() -> impl Generator<Yield = (), Return = ()> {
}
}
pub fn bar<T: 'static>(t: T) -> Box<Generator<Yield = T, Return = ()> + Unpin> {
pub fn bar<T: 'static>(t: T) -> Box<Generator<(), Yield = T, Return = ()> + Unpin> {
Box::new(|| {
yield t;
})

View File

@ -6,7 +6,7 @@ use std::pin::Pin;
fn main() {
let _b = {
let a = 3;
Pin::new(&mut || yield &a).resume()
Pin::new(&mut || yield &a).resume(())
//~^ ERROR: `a` does not live long enough
};

View File

@ -1,7 +1,7 @@
error[E0597]: `a` does not live long enough
--> $DIR/borrowing.rs:9:33
|
LL | Pin::new(&mut || yield &a).resume()
LL | Pin::new(&mut || yield &a).resume(())
| ----------^
| | |
| | borrowed value does not live long enough

View File

@ -35,9 +35,9 @@ fn t1() {
};
let n = A.load(Ordering::SeqCst);
Pin::new(&mut a).resume();
Pin::new(&mut a).resume(());
assert_eq!(A.load(Ordering::SeqCst), n + 1);
Pin::new(&mut a).resume();
Pin::new(&mut a).resume(());
assert_eq!(A.load(Ordering::SeqCst), n + 1);
}
@ -51,8 +51,8 @@ fn t2() {
};
let n = A.load(Ordering::SeqCst);
Pin::new(&mut a).resume();
Pin::new(&mut a).resume(());
assert_eq!(A.load(Ordering::SeqCst), n);
Pin::new(&mut a).resume();
Pin::new(&mut a).resume(());
assert_eq!(A.load(Ordering::SeqCst), n + 1);
}

View File

@ -7,10 +7,10 @@ use std::ops::{GeneratorState, Generator};
use std::pin::Pin;
fn finish<T>(mut amt: usize, mut t: T) -> T::Return
where T: Generator<Yield = ()> + Unpin,
where T: Generator<(), Yield = ()> + Unpin,
{
loop {
match Pin::new(&mut t).resume() {
match Pin::new(&mut t).resume(()) {
GeneratorState::Yielded(()) => amt = amt.checked_sub(1).unwrap(),
GeneratorState::Complete(ret) => {
assert_eq!(amt, 0);

View File

@ -37,7 +37,7 @@ fn main() {
};
loop {
match Pin::new(&mut a).resume() {
match Pin::new(&mut a).resume(()) {
GeneratorState::Complete(()) => break,
_ => (),
}

View File

@ -30,7 +30,7 @@ fn t1() {
};
let n = A.load(Ordering::SeqCst);
drop(Pin::new(&mut foo).resume());
drop(Pin::new(&mut foo).resume(()));
assert_eq!(A.load(Ordering::SeqCst), n);
drop(foo);
assert_eq!(A.load(Ordering::SeqCst), n + 1);
@ -43,7 +43,7 @@ fn t2() {
};
let n = A.load(Ordering::SeqCst);
drop(Pin::new(&mut foo).resume());
drop(Pin::new(&mut foo).resume(()));
assert_eq!(A.load(Ordering::SeqCst), n + 1);
drop(foo);
assert_eq!(A.load(Ordering::SeqCst), n + 1);

View File

@ -0,0 +1,33 @@
#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
use std::pin::Pin;
struct SetToNone<'a: 'b, 'b>(&'b mut Option<&'a i32>);
impl<'a, 'b> Drop for SetToNone<'a, 'b> {
fn drop(&mut self) {
*self.0 = None;
}
}
fn drop_using_generator() -> i32 {
let mut y = Some(&0);
let z = &mut y;
let r;
{
let mut g = move |r| {
let _s = SetToNone(r);
yield;
};
let mut g = Pin::new(&mut g);
g.as_mut().resume(z);
r = y.as_ref().unwrap();
//~^ ERROR cannot borrow `y` as immutable because it is also borrowed as mutable
}
**r
}
fn main() {
println!("{}", drop_using_generator());
}

View File

@ -0,0 +1,15 @@
error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
--> $DIR/dropck-resume.rs:25:13
|
LL | let z = &mut y;
| ------ mutable borrow occurs here
...
LL | r = y.as_ref().unwrap();
| ^ immutable borrow occurs here
LL |
LL | }
| - mutable borrow might be used here, when `g` is dropped and runs the destructor for generator
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.

View File

@ -15,6 +15,6 @@ fn main() {
let _d = ref_.take(); //~ ERROR `ref_` does not live long enough
yield;
};
Pin::new(&mut gen).resume();
Pin::new(&mut gen).resume(());
// drops the RefCell and then the Ref, leading to use-after-free
}

View File

@ -8,7 +8,7 @@ fn dangle(x: &mut i32) -> &'static mut i32 {
x
};
loop {
match Pin::new(&mut g).resume() {
match Pin::new(&mut g).resume(()) {
GeneratorState::Complete(c) => return c,
//~^ ERROR explicit lifetime required
GeneratorState::Yielded(_) => (),

View File

@ -2,14 +2,14 @@
#![feature(generators, generator_trait)]
use std::ops::{ Generator, GeneratorState };
use std::ops::{Generator, GeneratorState};
use std::pin::Pin;
fn foo(_: &str) -> String {
String::new()
}
fn bar(baz: String) -> impl Generator<Yield = String, Return = ()> {
fn bar(baz: String) -> impl Generator<(), Yield = String, Return = ()> {
move || {
yield foo(&baz);
}
@ -19,7 +19,7 @@ fn foo2(_: &str) -> Result<String, ()> {
Err(())
}
fn bar2(baz: String) -> impl Generator<Yield = String, Return = ()> {
fn bar2(baz: String) -> impl Generator<(), Yield = String, Return = ()> {
move || {
if let Ok(quux) = foo2(&baz) {
yield quux;
@ -28,6 +28,9 @@ fn bar2(baz: String) -> impl Generator<Yield = String, Return = ()> {
}
fn main() {
assert_eq!(Pin::new(&mut bar(String::new())).resume(), GeneratorState::Yielded(String::new()));
assert_eq!(Pin::new(&mut bar2(String::new())).resume(), GeneratorState::Complete(()));
assert_eq!(
Pin::new(&mut bar(String::new())).resume(()),
GeneratorState::Yielded(String::new())
);
assert_eq!(Pin::new(&mut bar2(String::new())).resume(()), GeneratorState::Complete(()));
}

View File

@ -18,12 +18,12 @@ fn drop_and_yield() {
String::new();
yield;
};
Box::pin(x).as_mut().resume();
Box::pin(x).as_mut().resume(());
let y = static || {
String::new();
yield;
};
Box::pin(y).as_mut().resume();
Box::pin(y).as_mut().resume(());
}
fn main() {

View File

@ -10,18 +10,18 @@ struct W<T>(T);
// This impl isn't safe in general, but the generator used in this test is movable
// so it won't cause problems.
impl<T: Generator<Return = ()> + Unpin> Iterator for W<T> {
impl<T: Generator<(), Return = ()> + Unpin> Iterator for W<T> {
type Item = T::Yield;
fn next(&mut self) -> Option<Self::Item> {
match Pin::new(&mut self.0).resume() {
match Pin::new(&mut self.0).resume(()) {
GeneratorState::Complete(..) => None,
GeneratorState::Yielded(v) => Some(v),
}
}
}
fn test() -> impl Generator<Return=(), Yield=u8> + Unpin {
fn test() -> impl Generator<(), Return=(), Yield=u8> + Unpin {
|| {
for i in 1..6 {
yield i

View File

@ -10,5 +10,5 @@ fn main() {
let mut a = || {
b(yield);
};
Pin::new(&mut a).resume();
Pin::new(&mut a).resume(());
}

View File

@ -11,7 +11,7 @@ fn main() {
yield 2;
};
match Pin::new(&mut sub_generator).resume() {
match Pin::new(&mut sub_generator).resume(()) {
GeneratorState::Yielded(x) => {
yield x;
}

View File

@ -1,8 +0,0 @@
#![feature(generators)]
fn main() {
let gen = |start| { //~ ERROR generators cannot have explicit parameters
//~^ ERROR type inside generator must be known in this context
yield;
};
}

View File

@ -1,21 +0,0 @@
error[E0628]: generators cannot have explicit parameters
--> $DIR/no-parameters-on-generators.rs:4:15
|
LL | let gen = |start| {
| ^^^^^^^
error[E0698]: type inside generator must be known in this context
--> $DIR/no-parameters-on-generators.rs:4:16
|
LL | let gen = |start| {
| ^^^^^ cannot infer type
|
note: the type is part of the generator because of this `yield`
--> $DIR/no-parameters-on-generators.rs:6:9
|
LL | yield;
| ^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0698`.

View File

@ -0,0 +1,37 @@
//! Tests that panics inside a generator will correctly drop the initial resume argument.
// run-pass
// ignore-wasm no unwind support
// ignore-emscripten no unwind support
#![feature(generators, generator_trait)]
use std::ops::Generator;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP: AtomicUsize = AtomicUsize::new(0);
struct Dropper {}
impl Drop for Dropper {
fn drop(&mut self) {
DROP.fetch_add(1, Ordering::SeqCst);
}
}
fn main() {
let mut gen = |_arg| {
if true {
panic!();
}
yield ();
};
let mut gen = Pin::new(&mut gen);
assert_eq!(DROP.load(Ordering::Acquire), 0);
let res = catch_unwind(AssertUnwindSafe(|| gen.as_mut().resume(Dropper {})));
assert!(res.is_err());
assert_eq!(DROP.load(Ordering::Acquire), 1);
}

View File

@ -35,7 +35,7 @@ fn main() {
assert_eq!(A.load(Ordering::SeqCst), 0);
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
Pin::new(&mut foo).resume()
Pin::new(&mut foo).resume(())
}));
assert!(res.is_err());
assert_eq!(A.load(Ordering::SeqCst), 1);
@ -50,7 +50,7 @@ fn main() {
assert_eq!(A.load(Ordering::SeqCst), 1);
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
Pin::new(&mut foo).resume()
Pin::new(&mut foo).resume(())
}));
assert!(res.is_err());
assert_eq!(A.load(Ordering::SeqCst), 1);

View File

@ -17,13 +17,13 @@ fn main() {
};
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
Pin::new(&mut foo).resume()
Pin::new(&mut foo).resume(())
}));
assert!(res.is_err());
for _ in 0..10 {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
Pin::new(&mut foo).resume()
Pin::new(&mut foo).resume(())
}));
assert!(res.is_err());
}

View File

@ -16,12 +16,12 @@ fn main() {
yield;
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}
match panic::catch_unwind(move || Pin::new(&mut foo).resume()) {
match panic::catch_unwind(move || Pin::new(&mut foo).resume(())) {
Ok(_) => panic!("generator successfully resumed"),
Err(_) => {}
}

View File

@ -0,0 +1,17 @@
//! Tests that we cannot produce a generator that accepts a resume argument
//! with any lifetime and then stores it across a `yield`.
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn test(a: impl for<'a> Generator<&'a mut bool>) {}
fn main() {
let gen = |arg: &mut bool| {
yield ();
*arg = true;
};
test(gen);
//~^ ERROR type mismatch in function arguments
}

View File

@ -0,0 +1,15 @@
error[E0631]: type mismatch in function arguments
--> $DIR/resume-arg-late-bound.rs:15:10
|
LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {}
| ---- ------------------------------- required by this bound in `test`
...
LL | test(gen);
| ^^^
| |
| expected signature of `for<'a> fn(&'a mut bool) -> _`
| found signature of `fn(&mut bool) -> _`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0631`.

View File

@ -0,0 +1,45 @@
// run-pass
#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP: AtomicUsize = AtomicUsize::new(0);
#[derive(PartialEq, Eq, Debug)]
struct Dropper(String);
impl Drop for Dropper {
fn drop(&mut self) {
DROP.fetch_add(1, Ordering::SeqCst);
}
}
fn main() {
let mut g = |mut _d| {
_d = yield;
_d
};
let mut g = Pin::new(&mut g);
assert_eq!(
g.as_mut().resume(Dropper(String::from("Hello world!"))),
GeneratorState::Yielded(())
);
assert_eq!(DROP.load(Ordering::Acquire), 0);
match g.as_mut().resume(Dropper(String::from("Number Two"))) {
GeneratorState::Complete(dropper) => {
assert_eq!(DROP.load(Ordering::Acquire), 1);
assert_eq!(dropper.0, "Number Two");
drop(dropper);
assert_eq!(DROP.load(Ordering::Acquire), 2);
}
_ => unreachable!(),
}
drop(g);
assert_eq!(DROP.load(Ordering::Acquire), 2);
}

View File

@ -0,0 +1,25 @@
//! This test ensures that a mutable reference cannot be passed as a resume argument twice.
#![feature(generators, generator_trait)]
use std::marker::Unpin;
use std::ops::{
Generator,
GeneratorState::{self, *},
};
use std::pin::Pin;
fn main() {
let mut thing = String::from("hello");
let mut gen = |r| {
if false {
yield r;
}
};
let mut gen = Pin::new(&mut gen);
gen.as_mut().resume(&mut thing);
gen.as_mut().resume(&mut thing);
//~^ cannot borrow `thing` as mutable more than once at a time
}

View File

@ -0,0 +1,14 @@
error[E0499]: cannot borrow `thing` as mutable more than once at a time
--> $DIR/retain-resume-ref.rs:23:25
|
LL | gen.as_mut().resume(&mut thing);
| ---------- first mutable borrow occurs here
LL | gen.as_mut().resume(&mut thing);
| ^^^^^^^^^^ second mutable borrow occurs here
LL |
LL | }
| - first borrow might be used here, when `gen` is dropped and runs the destructor for generator
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.

View File

@ -9,6 +9,6 @@ fn main() {
//~^ ERROR the size for values of type
yield s[..];
};
Pin::new(&mut gen).resume();
Pin::new(&mut gen).resume(());
//~^ ERROR the size for values of type
}

View File

@ -15,7 +15,7 @@ LL | | };
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/sized-yield.rs:12:23
|
LL | Pin::new(&mut gen).resume();
LL | Pin::new(&mut gen).resume(());
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`

View File

@ -0,0 +1,97 @@
// run-pass
#![feature(generators, generator_trait)]
use std::fmt::Debug;
use std::marker::Unpin;
use std::ops::{
Generator,
GeneratorState::{self, *},
};
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
fn drain<G: Generator<R, Yield = Y> + Unpin, R, Y>(
gen: &mut G,
inout: Vec<(R, GeneratorState<Y, G::Return>)>,
) where
Y: Debug + PartialEq,
G::Return: Debug + PartialEq,
{
let mut gen = Pin::new(gen);
for (input, out) in inout {
assert_eq!(gen.as_mut().resume(input), out);
}
}
static DROPS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
fn expect_drops<T>(expected_drops: usize, f: impl FnOnce() -> T) -> T {
DROPS.store(0, Ordering::SeqCst);
let res = f();
let actual_drops = DROPS.load(Ordering::SeqCst);
assert_eq!(actual_drops, expected_drops);
res
}
fn main() {
drain(
&mut |mut b| {
while b != 0 {
b = yield (b + 1);
}
-1
},
vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))],
);
expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))]));
expect_drops(6, || {
drain(
&mut |a| yield yield a,
vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))],
)
});
#[allow(unreachable_code)]
expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))]));
expect_drops(2, || {
drain(
&mut |a: DropMe| {
if false { yield () } else { a }
},
vec![(DropMe, Complete(DropMe))],
)
});
expect_drops(4, || {
drain(
#[allow(unused_assignments, unused_variables)]
&mut |mut a: DropMe| {
a = yield;
a = yield;
a = yield;
},
vec![
(DropMe, Yielded(())),
(DropMe, Yielded(())),
(DropMe, Yielded(())),
(DropMe, Complete(())),
],
)
});
}

View File

@ -17,7 +17,7 @@ fn simple() {
}
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}
@ -33,7 +33,7 @@ fn return_capture() {
a
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(ref s) if *s == "foo" => {}
s => panic!("bad state: {:?}", s),
}
@ -45,11 +45,11 @@ fn simple_yield() {
yield;
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(()) => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}
@ -62,11 +62,11 @@ fn yield_capture() {
yield b;
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(ref s) if *s == "foo" => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}
@ -79,11 +79,11 @@ fn simple_yield_value() {
return String::from("foo")
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(ref s) if *s == "bar" => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(ref s) if *s == "foo" => {}
s => panic!("bad state: {:?}", s),
}
@ -97,11 +97,11 @@ fn return_after_yield() {
return a
};
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(()) => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(ref s) if *s == "foo" => {}
s => panic!("bad state: {:?}", s),
}
@ -149,11 +149,11 @@ fn send_and_sync() {
fn send_over_threads() {
let mut foo = || { yield };
thread::spawn(move || {
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(()) => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}
@ -162,11 +162,11 @@ fn send_over_threads() {
let a = String::from("a");
let mut foo = || { yield a };
thread::spawn(move || {
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(ref s) if *s == "a" => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}

View File

@ -15,6 +15,6 @@ fn main() {
// Safety: We shadow the original generator variable so have no safe API to
// move it after this point.
let mut generator = unsafe { Pin::new_unchecked(&mut generator) };
assert_eq!(generator.as_mut().resume(), GeneratorState::Yielded(()));
assert_eq!(generator.as_mut().resume(), GeneratorState::Complete(()));
assert_eq!(generator.as_mut().resume(()), GeneratorState::Yielded(()));
assert_eq!(generator.as_mut().resume(()), GeneratorState::Complete(()));
}

View File

@ -0,0 +1,8 @@
#![feature(generators)]
fn main() {
|(), ()| {
//~^ error: too many parameters for a generator
yield;
};
}

View File

@ -0,0 +1,8 @@
error[E0628]: too many parameters for a generator (expected 0 or 1 parameters)
--> $DIR/too-many-parameters.rs:4:5
|
LL | |(), ()| {
| ^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,22 @@
//! Test that we get the expected type mismatch error instead of "closure is expected to take 0
//! arguments" (which got introduced after implementing resume arguments).
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn f<G: Generator>(_: G, _: G::Return) {}
fn main() {
f(
|a: u8| {
if false {
yield ();
} else {
a
//~^ error: `if` and `else` have incompatible types
}
},
0u8,
);
}

View File

@ -0,0 +1,19 @@
error[E0308]: `if` and `else` have incompatible types
--> $DIR/type-mismatch-error.rs:16:17
|
LL | / if false {
LL | | yield ();
| | ---------
| | | |
| | | help: consider removing this semicolon
| | expected because of this
LL | | } else {
LL | | a
| | ^ expected `()`, found `u8`
LL | |
LL | | }
| |_____________- `if` and `else` have incompatible types
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View File

@ -2,15 +2,15 @@
use std::ops::Generator;
fn foo() -> impl Generator<Return = i32> {
fn foo() -> impl Generator<Return = i32> { //~ ERROR type mismatch
|| {
if false {
return Ok(6); //~ ERROR mismatched types [E0308]
return Ok(6);
}
yield ();
5
5 //~ ERROR mismatched types [E0308]
}
}

View File

@ -1,12 +1,23 @@
error[E0308]: mismatched types
--> $DIR/type-mismatch-signature-deduction.rs:8:20
--> $DIR/type-mismatch-signature-deduction.rs:13:9
|
LL | return Ok(6);
| ^^^^^ expected `i32`, found enum `std::result::Result`
LL | 5
| ^ expected enum `std::result::Result`, found integer
|
= note: expected type `i32`
found enum `std::result::Result<{integer}, _>`
= note: expected type `std::result::Result<{integer}, _>`
found type `{integer}`
error: aborting due to previous error
error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:6:5: 14:6 _] as std::ops::Generator>::Return == i32`
--> $DIR/type-mismatch-signature-deduction.rs:5:13
|
LL | fn foo() -> impl Generator<Return = i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found `i32`
|
= note: expected enum `std::result::Result<{integer}, _>`
found type `i32`
= note: the return type of a function must have a statically known size
For more information about this error, try `rustc --explain E0308`.
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0308.
For more information about an error, try `rustc --explain E0271`.

View File

@ -10,5 +10,5 @@ use std::ops::Generator;
use std::pin::Pin;
fn main() {
Pin::new(&mut foo::foo()).resume();
Pin::new(&mut foo::foo()).resume(());
}

View File

@ -12,18 +12,18 @@ use std::pin::Pin;
fn main() {
let mut foo = xcrate::foo();
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}
let mut foo = xcrate::bar(3);
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Yielded(3) => {}
s => panic!("bad state: {:?}", s),
}
match Pin::new(&mut foo).resume() {
match Pin::new(&mut foo).resume(()) {
GeneratorState::Complete(()) => {}
s => panic!("bad state: {:?}", s),
}

View File

@ -43,7 +43,7 @@ fn yield_during_iter_borrowed_slice_3() {
yield p;
}
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn yield_during_iter_borrowed_slice_4() {
@ -56,7 +56,7 @@ fn yield_during_iter_borrowed_slice_4() {
}
};
println!("{}", x[0]); //~ ERROR
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn yield_during_range_iter() {
@ -69,7 +69,7 @@ fn yield_during_range_iter() {
yield x;
}
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn main() { }

View File

@ -16,7 +16,7 @@ LL | for p in &mut x {
...
LL | println!("{}", x[0]);
| ^ immutable borrow occurs here
LL | Pin::new(&mut b).resume();
LL | Pin::new(&mut b).resume(());
| ------ mutable borrow later used here
error: aborting due to 2 previous errors

View File

@ -15,7 +15,7 @@ fn borrow_local_inline() {
yield();
println!("{}", a);
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn borrow_local_inline_done() {
@ -26,7 +26,7 @@ fn borrow_local_inline_done() {
}
yield();
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn borrow_local() {
@ -43,7 +43,7 @@ fn borrow_local() {
println!("{}", b);
}
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn main() { }

View File

@ -12,7 +12,7 @@ fn reborrow_shared_ref(x: &i32) {
yield();
println!("{}", a);
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn reborrow_mutable_ref(x: &mut i32) {
@ -23,7 +23,7 @@ fn reborrow_mutable_ref(x: &mut i32) {
yield();
println!("{}", a);
};
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn reborrow_mutable_ref_2(x: &mut i32) {
@ -34,7 +34,7 @@ fn reborrow_mutable_ref_2(x: &mut i32) {
println!("{}", a);
};
println!("{}", x); //~ ERROR
Pin::new(&mut b).resume();
Pin::new(&mut b).resume(());
}
fn main() { }

View File

@ -8,7 +8,7 @@ LL | let a = &mut *x;
...
LL | println!("{}", x);
| ^ second borrow occurs here
LL | Pin::new(&mut b).resume();
LL | Pin::new(&mut b).resume(());
| ------ first borrow later used here
error: aborting due to previous error

View File

@ -15,7 +15,7 @@ where
type Item = G::Yield;
fn next(&mut self) -> Option<Self::Item> {
match Pin::new(&mut self.0).resume() {
match Pin::new(&mut self.0).resume(()) {
Yielded(y) => Some(y),
_ => None
}