Skip to content

Commit d735ede

Browse files
committed
Auto merge of #69302 - jonas-schievink:yield-needs-storage, r=Zoxc
Fix generator miscompilations Fixes #69039 r? @Zoxc
2 parents 1d7e818 + fc2702c commit d735ede

File tree

5 files changed

+109
-22
lines changed

5 files changed

+109
-22
lines changed

src/librustc/mir/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -519,12 +519,12 @@ macro_rules! make_mir_visitor {
519519
resume_arg,
520520
drop: _,
521521
} => {
522+
self.visit_operand(value, source_location);
522523
self.visit_place(
523524
resume_arg,
524525
PlaceContext::MutatingUse(MutatingUseContext::Store),
525526
source_location,
526527
);
527-
self.visit_operand(value, source_location);
528528
}
529529

530530
}

src/librustc_mir/dataflow/impls/storage_liveness.rs

+59-17
Original file line numberDiff line numberDiff line change
@@ -118,18 +118,25 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
118118
self.borrowed_locals.borrow().analysis().statement_effect(sets, stmt, loc);
119119

120120
// If a place is assigned to in a statement, it needs storage for that statement.
121-
match stmt.kind {
122-
StatementKind::StorageDead(l) => sets.kill(l),
123-
StatementKind::Assign(box (ref place, _))
124-
| StatementKind::SetDiscriminant { box ref place, .. } => {
121+
match &stmt.kind {
122+
StatementKind::StorageDead(l) => sets.kill(*l),
123+
StatementKind::Assign(box (place, _))
124+
| StatementKind::SetDiscriminant { box place, .. } => {
125125
sets.gen(place.local);
126126
}
127-
StatementKind::InlineAsm(box InlineAsm { ref outputs, .. }) => {
127+
StatementKind::InlineAsm(box InlineAsm { outputs, .. }) => {
128128
for place in &**outputs {
129129
sets.gen(place.local);
130130
}
131131
}
132-
_ => (),
132+
133+
// Nothing to do for these. Match exhaustively so this fails to compile when new
134+
// variants are added.
135+
StatementKind::AscribeUserType(..)
136+
| StatementKind::FakeRead(..)
137+
| StatementKind::Nop
138+
| StatementKind::Retag(..)
139+
| StatementKind::StorageLive(..) => {}
133140
}
134141
}
135142

@@ -145,23 +152,58 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
145152
// If a place is borrowed in a terminator, it needs storage for that terminator.
146153
self.borrowed_locals.borrow().analysis().terminator_effect(sets, terminator, loc);
147154

148-
if let TerminatorKind::Call { destination: Some((place, _)), .. } = terminator.kind {
149-
sets.gen(place.local);
155+
match &terminator.kind {
156+
TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. }
157+
| TerminatorKind::Yield { resume_arg: Place { local, .. }, .. } => {
158+
sets.gen(*local);
159+
}
160+
161+
// Nothing to do for these. Match exhaustively so this fails to compile when new
162+
// variants are added.
163+
TerminatorKind::Call { destination: None, .. }
164+
| TerminatorKind::Abort
165+
| TerminatorKind::Assert { .. }
166+
| TerminatorKind::Drop { .. }
167+
| TerminatorKind::DropAndReplace { .. }
168+
| TerminatorKind::FalseEdges { .. }
169+
| TerminatorKind::FalseUnwind { .. }
170+
| TerminatorKind::GeneratorDrop
171+
| TerminatorKind::Goto { .. }
172+
| TerminatorKind::Resume
173+
| TerminatorKind::Return
174+
| TerminatorKind::SwitchInt { .. }
175+
| TerminatorKind::Unreachable => {}
150176
}
151177
}
152178

153179
fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
154-
// For call terminators the destination requires storage for the call
155-
// and after the call returns successfully, but not after a panic.
156-
// Since `propagate_call_unwind` doesn't exist, we have to kill the
157-
// destination here, and then gen it again in `propagate_call_return`.
158-
if let TerminatorKind::Call { destination: Some((ref place, _)), .. } =
159-
self.body[loc.block].terminator().kind
160-
{
161-
if let Some(local) = place.as_local() {
162-
sets.kill(local);
180+
match &self.body[loc.block].terminator().kind {
181+
// For call terminators the destination requires storage for the call
182+
// and after the call returns successfully, but not after a panic.
183+
// Since `propagate_call_unwind` doesn't exist, we have to kill the
184+
// destination here, and then gen it again in `propagate_call_return`.
185+
TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. } => {
186+
sets.kill(*local);
163187
}
188+
189+
// Nothing to do for these. Match exhaustively so this fails to compile when new
190+
// variants are added.
191+
TerminatorKind::Call { destination: None, .. }
192+
| TerminatorKind::Yield { .. }
193+
| TerminatorKind::Abort
194+
| TerminatorKind::Assert { .. }
195+
| TerminatorKind::Drop { .. }
196+
| TerminatorKind::DropAndReplace { .. }
197+
| TerminatorKind::FalseEdges { .. }
198+
| TerminatorKind::FalseUnwind { .. }
199+
| TerminatorKind::GeneratorDrop
200+
| TerminatorKind::Goto { .. }
201+
| TerminatorKind::Resume
202+
| TerminatorKind::Return
203+
| TerminatorKind::SwitchInt { .. }
204+
| TerminatorKind::Unreachable => {}
164205
}
206+
165207
self.check_for_move(sets, loc);
166208
}
167209

src/librustc_mir/dataflow/move_paths/builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
381381
}
382382

383383
TerminatorKind::Yield { ref value, resume_arg: ref place, .. } => {
384+
self.gather_operand(value);
384385
self.create_move_path(place);
385386
self.gather_init(place.as_ref(), InitKind::Deep);
386-
self.gather_operand(value);
387387
}
388388

389389
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {

src/librustc_mir/transform/generator.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,24 @@ fn self_arg() -> Local {
186186
Local::new(1)
187187
}
188188

189-
/// Generator have not been resumed yet
189+
/// Generator has not been resumed yet.
190190
const UNRESUMED: usize = GeneratorSubsts::UNRESUMED;
191-
/// Generator has returned / is completed
191+
/// Generator has returned / is completed.
192192
const RETURNED: usize = GeneratorSubsts::RETURNED;
193-
/// Generator has been poisoned
193+
/// Generator has panicked and is poisoned.
194194
const POISONED: usize = GeneratorSubsts::POISONED;
195195

196+
/// A `yield` point in the generator.
196197
struct SuspensionPoint<'tcx> {
198+
/// State discriminant used when suspending or resuming at this point.
197199
state: usize,
200+
/// The block to jump to after resumption.
198201
resume: BasicBlock,
202+
/// Where to move the resume argument after resumption.
199203
resume_arg: Place<'tcx>,
204+
/// Which block to jump to if the generator is dropped in this state.
200205
drop: Option<BasicBlock>,
206+
/// Set of locals that have live storage while at this suspension point.
201207
storage_liveness: liveness::LiveVarSet,
202208
}
203209

@@ -325,6 +331,15 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
325331
// Yield
326332
let state = 3 + self.suspension_points.len();
327333

334+
// The resume arg target location might itself be remapped if its base local is
335+
// live across a yield.
336+
let resume_arg =
337+
if let Some(&(ty, variant, idx)) = self.remap.get(&resume_arg.local) {
338+
self.make_field(variant, idx, ty)
339+
} else {
340+
resume_arg
341+
};
342+
328343
self.suspension_points.push(SuspensionPoint {
329344
state,
330345
resume,

src/test/ui/generator/issue-69039.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-pass
2+
3+
#![feature(generators, generator_trait)]
4+
5+
use std::ops::{Generator, GeneratorState};
6+
7+
fn my_scenario() -> impl Generator<String, Yield = &'static str, Return = String> {
8+
|_arg: String| {
9+
let my_name = yield "What is your name?";
10+
let my_mood = yield "How are you feeling?";
11+
format!("{} is {}", my_name.trim(), my_mood.trim())
12+
}
13+
}
14+
15+
fn main() {
16+
let mut my_session = Box::pin(my_scenario());
17+
18+
assert_eq!(
19+
my_session.as_mut().resume("_arg".to_string()),
20+
GeneratorState::Yielded("What is your name?")
21+
);
22+
assert_eq!(
23+
my_session.as_mut().resume("Your Name".to_string()),
24+
GeneratorState::Yielded("How are you feeling?")
25+
);
26+
assert_eq!(
27+
my_session.as_mut().resume("Sensory Organs".to_string()),
28+
GeneratorState::Complete("Your Name is Sensory Organs".to_string())
29+
);
30+
}

0 commit comments

Comments
 (0)