Skip to content

Commit 3d292b7

Browse files
committed
Auto merge of #47845 - Zoxc:gen-fixes, r=nikomatsakis
Generator bugfixes r? @nikomatsakis
2 parents aa0a5a8 + 6c66e11 commit 3d292b7

File tree

21 files changed

+342
-52
lines changed

21 files changed

+342
-52
lines changed

src/librustc/middle/region.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,13 @@ impl<'tcx> Visitor<'tcx> for ExprLocatorVisitor {
467467
}
468468

469469
fn visit_pat(&mut self, pat: &'tcx Pat) {
470+
intravisit::walk_pat(self, pat);
471+
470472
self.expr_and_pat_count += 1;
471473

472-
intravisit::walk_pat(self, pat);
474+
if pat.id == self.id {
475+
self.result = Some(self.expr_and_pat_count);
476+
}
473477
}
474478

475479
fn visit_expr(&mut self, expr: &'tcx Expr) {
@@ -814,7 +818,8 @@ impl<'tcx> ScopeTree {
814818

815819
/// Checks whether the given scope contains a `yield`. If so,
816820
/// returns `Some((span, expr_count))` with the span of a yield we found and
817-
/// the number of expressions appearing before the `yield` in the body.
821+
/// the number of expressions and patterns appearing before the `yield` in the body + 1.
822+
/// If there a are multiple yields in a scope, the one with the highest number is returned.
818823
pub fn yield_in_scope(&self, scope: Scope) -> Option<(Span, usize)> {
819824
self.yield_in_scope.get(&scope).cloned()
820825
}

src/librustc/traits/error_reporting.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
12661266
err.note("the return type of a function must have a \
12671267
statically known size");
12681268
}
1269+
ObligationCauseCode::SizedYieldType => {
1270+
err.note("the yield type of a generator must have a \
1271+
statically known size");
1272+
}
12691273
ObligationCauseCode::AssignmentLhsSized => {
12701274
err.note("the left-hand-side of an assignment must have a statically known size");
12711275
}

src/librustc/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ pub enum ObligationCauseCode<'tcx> {
151151
VariableType(ast::NodeId),
152152
/// Return type must be Sized
153153
SizedReturnType,
154+
/// Yield type must be Sized
155+
SizedYieldType,
154156
/// [T,..n] --> T must be Copy
155157
RepeatVec,
156158

src/librustc/traits/structural_impls.rs

+3
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
209209
super::VariableType(id) => Some(super::VariableType(id)),
210210
super::ReturnType(id) => Some(super::ReturnType(id)),
211211
super::SizedReturnType => Some(super::SizedReturnType),
212+
super::SizedYieldType => Some(super::SizedYieldType),
212213
super::RepeatVec => Some(super::RepeatVec),
213214
super::FieldSized(item) => Some(super::FieldSized(item)),
214215
super::ConstSized => Some(super::ConstSized),
@@ -526,6 +527,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
526527
super::VariableType(_) |
527528
super::ReturnType(_) |
528529
super::SizedReturnType |
530+
super::SizedYieldType |
529531
super::ReturnNoExpression |
530532
super::RepeatVec |
531533
super::FieldSized(_) |
@@ -574,6 +576,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
574576
super::VariableType(_) |
575577
super::ReturnType(_) |
576578
super::SizedReturnType |
579+
super::SizedYieldType |
577580
super::ReturnNoExpression |
578581
super::RepeatVec |
579582
super::FieldSized(_) |

src/librustc/ty/sty.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -390,14 +390,21 @@ impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> {
390390
state.map(move |d| d.ty.subst(tcx, self.substs))
391391
}
392392

393+
/// This is the types of the fields of a generate which
394+
/// is available before the generator transformation.
395+
/// It includes the upvars and the state discriminant which is u32.
396+
pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
397+
impl Iterator<Item=Ty<'tcx>> + 'a
398+
{
399+
self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
400+
}
401+
393402
/// This is the types of all the fields stored in a generator.
394403
/// It includes the upvars, state types and the state discriminant which is u32.
395404
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
396405
impl Iterator<Item=Ty<'tcx>> + 'a
397406
{
398-
let upvars = self.upvar_tys(def_id, tcx);
399-
let state = self.state_tys(def_id, tcx);
400-
upvars.chain(iter::once(tcx.types.u32)).chain(state)
407+
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
401408
}
402409
}
403410

src/librustc_mir/borrow_check/error_reporting.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -746,12 +746,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
746746
self.describe_field_from_ty(&tnm.ty, field)
747747
}
748748
ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
749-
ty::TyClosure(closure_def_id, _) => {
749+
ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
750750
// Convert the def-id into a node-id. node-ids are only valid for
751751
// the local code in the current crate, so this returns an `Option` in case
752752
// the closure comes from another crate. But in that case we wouldn't
753753
// be borrowck'ing it, so we can just unwrap:
754-
let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
754+
let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
755755
let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
756756

757757
self.tcx.hir.name(freevar.var_id()).to_string()

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -533,15 +533,17 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
533533
}
534534
}
535535
ty::TyGenerator(def_id, substs, _) => {
536-
// Try upvars first. `field_tys` requires final optimized MIR.
537-
if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field.index()) {
536+
// Try pre-transform fields first (upvars and current state)
537+
if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field.index()) {
538538
return Ok(ty);
539539
}
540540

541+
// Then try `field_tys` which contains all the fields, but it
542+
// requires the final optimized MIR.
541543
return match substs.field_tys(def_id, tcx).nth(field.index()) {
542544
Some(ty) => Ok(ty),
543545
None => Err(FieldAccessError::OutOfRange {
544-
field_count: substs.field_tys(def_id, tcx).count() + 1,
546+
field_count: substs.field_tys(def_id, tcx).count(),
545547
}),
546548
};
547549
}
@@ -1233,13 +1235,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
12331235
}
12341236
}
12351237
AggregateKind::Generator(def_id, substs, _) => {
1236-
if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field_index) {
1238+
// Try pre-transform fields first (upvars and current state)
1239+
if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field_index) {
12371240
Ok(ty)
12381241
} else {
1242+
// Then try `field_tys` which contains all the fields, but it
1243+
// requires the final optimized MIR.
12391244
match substs.field_tys(def_id, tcx).nth(field_index) {
12401245
Some(ty) => Ok(ty),
12411246
None => Err(FieldAccessError::OutOfRange {
1242-
field_count: substs.field_tys(def_id, tcx).count() + 1,
1247+
field_count: substs.field_tys(def_id, tcx).count(),
12431248
}),
12441249
}
12451250
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub use super::*;
12+
13+
use rustc::mir::*;
14+
use rustc::mir::visit::Visitor;
15+
use dataflow::BitDenotation;
16+
17+
/// This calculates if any part of a MIR local could have previously been borrowed.
18+
/// This means that once a local has been borrowed, its bit will always be set
19+
/// from that point and onwards, even if the borrow ends. You could also think of this
20+
/// as computing the lifetimes of infinite borrows.
21+
/// This is used to compute which locals are live during a yield expression for
22+
/// immovable generators.
23+
#[derive(Copy, Clone)]
24+
pub struct HaveBeenBorrowedLocals<'a, 'tcx: 'a> {
25+
mir: &'a Mir<'tcx>,
26+
}
27+
28+
impl<'a, 'tcx: 'a> HaveBeenBorrowedLocals<'a, 'tcx> {
29+
pub fn new(mir: &'a Mir<'tcx>)
30+
-> Self {
31+
HaveBeenBorrowedLocals { mir: mir }
32+
}
33+
34+
pub fn mir(&self) -> &Mir<'tcx> {
35+
self.mir
36+
}
37+
}
38+
39+
impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> {
40+
type Idx = Local;
41+
fn name() -> &'static str { "has_been_borrowed_locals" }
42+
fn bits_per_block(&self) -> usize {
43+
self.mir.local_decls.len()
44+
}
45+
46+
fn start_block_effect(&self, _sets: &mut IdxSet<Local>) {
47+
// Nothing is borrowed on function entry
48+
}
49+
50+
fn statement_effect(&self,
51+
sets: &mut BlockSets<Local>,
52+
loc: Location) {
53+
BorrowedLocalsVisitor {
54+
sets,
55+
}.visit_statement(loc.block, &self.mir[loc.block].statements[loc.statement_index], loc);
56+
}
57+
58+
fn terminator_effect(&self,
59+
sets: &mut BlockSets<Local>,
60+
loc: Location) {
61+
BorrowedLocalsVisitor {
62+
sets,
63+
}.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc);
64+
}
65+
66+
fn propagate_call_return(&self,
67+
_in_out: &mut IdxSet<Local>,
68+
_call_bb: mir::BasicBlock,
69+
_dest_bb: mir::BasicBlock,
70+
_dest_place: &mir::Place) {
71+
// Nothing to do when a call returns successfully
72+
}
73+
}
74+
75+
impl<'a, 'tcx> BitwiseOperator for HaveBeenBorrowedLocals<'a, 'tcx> {
76+
#[inline]
77+
fn join(&self, pred1: usize, pred2: usize) -> usize {
78+
pred1 | pred2 // "maybe" means we union effects of both preds
79+
}
80+
}
81+
82+
impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> {
83+
#[inline]
84+
fn bottom_value() -> bool {
85+
false // bottom = unborrowed
86+
}
87+
}
88+
89+
struct BorrowedLocalsVisitor<'b, 'c: 'b> {
90+
sets: &'b mut BlockSets<'c, Local>,
91+
}
92+
93+
fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
94+
match *place {
95+
Place::Local(l) => Some(l),
96+
Place::Static(..) => None,
97+
Place::Projection(ref proj) => {
98+
match proj.elem {
99+
ProjectionElem::Deref => None,
100+
_ => find_local(&proj.base)
101+
}
102+
}
103+
}
104+
}
105+
106+
impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> {
107+
fn visit_rvalue(&mut self,
108+
rvalue: &Rvalue<'tcx>,
109+
location: Location) {
110+
if let Rvalue::Ref(_, _, ref place) = *rvalue {
111+
if let Some(local) = find_local(place) {
112+
self.sets.gen(&local);
113+
}
114+
}
115+
116+
self.super_rvalue(rvalue, location)
117+
}
118+
}

src/librustc_mir/dataflow/impls/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ mod storage_liveness;
3333

3434
pub use self::storage_liveness::*;
3535

36+
mod borrowed_locals;
37+
38+
pub use self::borrowed_locals::*;
39+
3640
#[allow(dead_code)]
3741
pub(super) mod borrows;
3842

src/librustc_mir/dataflow/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
3030
pub use self::impls::{DefinitelyInitializedPlaces, MovingOutStatements};
3131
pub use self::impls::EverInitializedPlaces;
3232
pub use self::impls::borrows::{Borrows, BorrowData};
33+
pub use self::impls::HaveBeenBorrowedLocals;
3334
pub(crate) use self::impls::borrows::{ActiveBorrows, Reservations, ReserveOrActivateIndex};
3435
pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
3536
pub(crate) use self::drop_flag_effects::*;

src/librustc_mir/transform/generator.rs

+51-17
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ use std::mem;
7878
use transform::{MirPass, MirSource};
7979
use transform::simplify;
8080
use transform::no_landing_pads::no_landing_pads;
81-
use dataflow::{do_dataflow, DebugFormatted, MaybeStorageLive, state_for_location};
81+
use dataflow::{do_dataflow, DebugFormatted, state_for_location};
82+
use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals};
8283

8384
pub struct StateTransform;
8485

@@ -369,17 +370,33 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
369370
HashMap<BasicBlock, liveness::LocalSet>) {
370371
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
371372
let node_id = tcx.hir.as_local_node_id(source.def_id).unwrap();
372-
let analysis = MaybeStorageLive::new(mir);
373+
374+
// Calculate when MIR locals have live storage. This gives us an upper bound of their
375+
// lifetimes.
376+
let storage_live_analysis = MaybeStorageLive::new(mir);
373377
let storage_live =
374-
do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis,
378+
do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, storage_live_analysis,
375379
|bd, p| DebugFormatted::new(&bd.mir().local_decls[p]));
376380

381+
// Find the MIR locals which do not use StorageLive/StorageDead statements.
382+
// The storage of these locals are always live.
377383
let mut ignored = StorageIgnored(IdxSetBuf::new_filled(mir.local_decls.len()));
378384
ignored.visit_mir(mir);
379385

380-
let mut borrowed_locals = BorrowedLocals(IdxSetBuf::new_empty(mir.local_decls.len()));
381-
borrowed_locals.visit_mir(mir);
386+
// Calculate the MIR locals which have been previously
387+
// borrowed (even if they are still active).
388+
// This is only used for immovable generators.
389+
let borrowed_locals = if !movable {
390+
let analysis = HaveBeenBorrowedLocals::new(mir);
391+
let result =
392+
do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis,
393+
|bd, p| DebugFormatted::new(&bd.mir().local_decls[p]));
394+
Some((analysis, result))
395+
} else {
396+
None
397+
};
382398

399+
// Calculate the liveness of MIR locals ignoring borrows.
383400
let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
384401
let mut liveness = liveness::liveness_of_locals(mir, LivenessMode {
385402
include_regular_use: true,
@@ -396,24 +413,41 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
396413
statement_index: data.statements.len(),
397414
};
398415

399-
let storage_liveness = state_for_location(loc, &analysis, &storage_live, mir);
416+
if let Some((ref analysis, ref result)) = borrowed_locals {
417+
let borrowed_locals = state_for_location(loc,
418+
analysis,
419+
result,
420+
mir);
421+
// The `liveness` variable contains the liveness of MIR locals ignoring borrows.
422+
// This is correct for movable generators since borrows cannot live across
423+
// suspension points. However for immovable generators we need to account for
424+
// borrows, so we conseratively assume that all borrowed locals live forever.
425+
// To do this we just union our `liveness` result with `borrowed_locals`, which
426+
// contains all the locals which has been borrowed before this suspension point.
427+
// If a borrow is converted to a raw reference, we must also assume that it lives
428+
// forever. Note that the final liveness is still bounded by the storage liveness
429+
// of the local, which happens using the `intersect` operation below.
430+
liveness.outs[block].union(&borrowed_locals);
431+
}
432+
433+
let mut storage_liveness = state_for_location(loc,
434+
&storage_live_analysis,
435+
&storage_live,
436+
mir);
400437

438+
// Store the storage liveness for later use so we can restore the state
439+
// after a suspension point
401440
storage_liveness_map.insert(block, storage_liveness.clone());
402441

403-
let mut live_locals = storage_liveness;
404-
405442
// Mark locals without storage statements as always having live storage
406-
live_locals.union(&ignored.0);
443+
storage_liveness.union(&ignored.0);
407444

408-
if !movable {
409-
// For immovable generators we consider borrowed locals to always be live.
410-
// This effectively makes those locals use just the storage liveness.
411-
liveness.outs[block].union(&borrowed_locals.0);
412-
}
445+
// Locals live are live at this point only if they are used across
446+
// suspension points (the `liveness` variable)
447+
// and their storage is live (the `storage_liveness` variable)
448+
storage_liveness.intersect(&liveness.outs[block]);
413449

414-
// Locals live are live at this point only if they are used across suspension points
415-
// and their storage is live
416-
live_locals.intersect(&liveness.outs[block]);
450+
let live_locals = storage_liveness;
417451

418452
// Add the locals life at this suspension point to the set of locals which live across
419453
// any suspension points

0 commit comments

Comments
 (0)