提交 3d292b79 编写于 作者: B bors

Auto merge of #47845 - Zoxc:gen-fixes, r=nikomatsakis

Generator bugfixes

r? @nikomatsakis
......@@ -467,9 +467,13 @@ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
}
fn visit_pat(&mut self, pat: &'tcx Pat) {
intravisit::walk_pat(self, pat);
self.expr_and_pat_count += 1;
intravisit::walk_pat(self, pat);
if pat.id == self.id {
self.result = Some(self.expr_and_pat_count);
}
}
fn visit_expr(&mut self, expr: &'tcx Expr) {
......@@ -814,7 +818,8 @@ pub fn free_scope<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, fr: &ty::FreeReg
/// Checks whether the given scope contains a `yield`. If so,
/// returns `Some((span, expr_count))` with the span of a yield we found and
/// the number of expressions appearing before the `yield` in the body.
/// the number of expressions and patterns appearing before the `yield` in the body + 1.
/// If there a are multiple yields in a scope, the one with the highest number is returned.
pub fn yield_in_scope(&self, scope: Scope) -> Option<(Span, usize)> {
self.yield_in_scope.get(&scope).cloned()
}
......
......@@ -1266,6 +1266,10 @@ fn note_obligation_cause_code<T>(&self,
err.note("the return type of a function must have a \
statically known size");
}
ObligationCauseCode::SizedYieldType => {
err.note("the yield type of a generator must have a \
statically known size");
}
ObligationCauseCode::AssignmentLhsSized => {
err.note("the left-hand-side of an assignment must have a statically known size");
}
......
......@@ -151,6 +151,8 @@ pub enum ObligationCauseCode<'tcx> {
VariableType(ast::NodeId),
/// Return type must be Sized
SizedReturnType,
/// Yield type must be Sized
SizedYieldType,
/// [T,..n] --> T must be Copy
RepeatVec,
......
......@@ -209,6 +209,7 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
super::VariableType(id) => Some(super::VariableType(id)),
super::ReturnType(id) => Some(super::ReturnType(id)),
super::SizedReturnType => Some(super::SizedReturnType),
super::SizedYieldType => Some(super::SizedYieldType),
super::RepeatVec => Some(super::RepeatVec),
super::FieldSized(item) => Some(super::FieldSized(item)),
super::ConstSized => Some(super::ConstSized),
......@@ -526,6 +527,7 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
super::VariableType(_) |
super::ReturnType(_) |
super::SizedReturnType |
super::SizedYieldType |
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized(_) |
......@@ -574,6 +576,7 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
super::VariableType(_) |
super::ReturnType(_) |
super::SizedReturnType |
super::SizedYieldType |
super::ReturnNoExpression |
super::RepeatVec |
super::FieldSized(_) |
......
......@@ -390,14 +390,21 @@ impl Iterator<Item=Ty<'tcx>> + 'a
state.map(move |d| d.ty.subst(tcx, self.substs))
}
/// This is the types of the fields of a generate which
/// is available before the generator transformation.
/// It includes the upvars and the state discriminant which is u32.
pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
{
self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
}
/// This is the types of all the fields stored in a generator.
/// It includes the upvars, state types and the state discriminant which is u32.
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
{
let upvars = self.upvar_tys(def_id, tcx);
let state = self.state_tys(def_id, tcx);
upvars.chain(iter::once(tcx.types.u32)).chain(state)
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
}
}
......
......@@ -746,12 +746,12 @@ fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
self.describe_field_from_ty(&tnm.ty, field)
}
ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
ty::TyClosure(closure_def_id, _) => {
ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
// Convert the def-id into a node-id. node-ids are only valid for
// the local code in the current crate, so this returns an `Option` in case
// the closure comes from another crate. But in that case we wouldn't
// be borrowck'ing it, so we can just unwrap:
let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
self.tcx.hir.name(freevar.var_id()).to_string()
......
......@@ -533,15 +533,17 @@ fn field_ty(
}
}
ty::TyGenerator(def_id, substs, _) => {
// Try upvars first. `field_tys` requires final optimized MIR.
if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field.index()) {
// Try pre-transform fields first (upvars and current state)
if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field.index()) {
return Ok(ty);
}
// Then try `field_tys` which contains all the fields, but it
// requires the final optimized MIR.
return match substs.field_tys(def_id, tcx).nth(field.index()) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: substs.field_tys(def_id, tcx).count() + 1,
field_count: substs.field_tys(def_id, tcx).count(),
}),
};
}
......@@ -1233,13 +1235,16 @@ fn aggregate_field_ty(
}
}
AggregateKind::Generator(def_id, substs, _) => {
if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field_index) {
// Try pre-transform fields first (upvars and current state)
if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field_index) {
Ok(ty)
} else {
// Then try `field_tys` which contains all the fields, but it
// requires the final optimized MIR.
match substs.field_tys(def_id, tcx).nth(field_index) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: substs.field_tys(def_id, tcx).count() + 1,
field_count: substs.field_tys(def_id, tcx).count(),
}),
}
}
......
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub use super::*;
use rustc::mir::*;
use rustc::mir::visit::Visitor;
use dataflow::BitDenotation;
/// This calculates if any part of a MIR local could have previously been borrowed.
/// This means that once a local has been borrowed, its bit will always be set
/// from that point and onwards, even if the borrow ends. You could also think of this
/// as computing the lifetimes of infinite borrows.
/// This is used to compute which locals are live during a yield expression for
/// immovable generators.
#[derive(Copy, Clone)]
pub struct HaveBeenBorrowedLocals<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
}
impl<'a, 'tcx: 'a> HaveBeenBorrowedLocals<'a, 'tcx> {
pub fn new(mir: &'a Mir<'tcx>)
-> Self {
HaveBeenBorrowedLocals { mir: mir }
}
pub fn mir(&self) -> &Mir<'tcx> {
self.mir
}
}
impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> {
type Idx = Local;
fn name() -> &'static str { "has_been_borrowed_locals" }
fn bits_per_block(&self) -> usize {
self.mir.local_decls.len()
}
fn start_block_effect(&self, _sets: &mut IdxSet<Local>) {
// Nothing is borrowed on function entry
}
fn statement_effect(&self,
sets: &mut BlockSets<Local>,
loc: Location) {
BorrowedLocalsVisitor {
sets,
}.visit_statement(loc.block, &self.mir[loc.block].statements[loc.statement_index], loc);
}
fn terminator_effect(&self,
sets: &mut BlockSets<Local>,
loc: Location) {
BorrowedLocalsVisitor {
sets,
}.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc);
}
fn propagate_call_return(&self,
_in_out: &mut IdxSet<Local>,
_call_bb: mir::BasicBlock,
_dest_bb: mir::BasicBlock,
_dest_place: &mir::Place) {
// Nothing to do when a call returns successfully
}
}
impl<'a, 'tcx> BitwiseOperator for HaveBeenBorrowedLocals<'a, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // "maybe" means we union effects of both preds
}
}
impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = unborrowed
}
}
struct BorrowedLocalsVisitor<'b, 'c: 'b> {
sets: &'b mut BlockSets<'c, Local>,
}
fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
match *place {
Place::Local(l) => Some(l),
Place::Static(..) => None,
Place::Projection(ref proj) => {
match proj.elem {
ProjectionElem::Deref => None,
_ => find_local(&proj.base)
}
}
}
}
impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Ref(_, _, ref place) = *rvalue {
if let Some(local) = find_local(place) {
self.sets.gen(&local);
}
}
self.super_rvalue(rvalue, location)
}
}
......@@ -33,6 +33,10 @@
pub use self::storage_liveness::*;
mod borrowed_locals;
pub use self::borrowed_locals::*;
#[allow(dead_code)]
pub(super) mod borrows;
......
......@@ -30,6 +30,7 @@
pub use self::impls::{DefinitelyInitializedPlaces, MovingOutStatements};
pub use self::impls::EverInitializedPlaces;
pub use self::impls::borrows::{Borrows, BorrowData};
pub use self::impls::HaveBeenBorrowedLocals;
pub(crate) use self::impls::borrows::{ActiveBorrows, Reservations, ReserveOrActivateIndex};
pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
pub(crate) use self::drop_flag_effects::*;
......
......@@ -78,7 +78,8 @@
use transform::{MirPass, MirSource};
use transform::simplify;
use transform::no_landing_pads::no_landing_pads;
use dataflow::{do_dataflow, DebugFormatted, MaybeStorageLive, state_for_location};
use dataflow::{do_dataflow, DebugFormatted, state_for_location};
use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals};
pub struct StateTransform;
......@@ -369,17 +370,33 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
HashMap<BasicBlock, liveness::LocalSet>) {
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let node_id = tcx.hir.as_local_node_id(source.def_id).unwrap();
let analysis = MaybeStorageLive::new(mir);
// Calculate when MIR locals have live storage. This gives us an upper bound of their
// lifetimes.
let storage_live_analysis = MaybeStorageLive::new(mir);
let storage_live =
do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis,
do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, storage_live_analysis,
|bd, p| DebugFormatted::new(&bd.mir().local_decls[p]));
// Find the MIR locals which do not use StorageLive/StorageDead statements.
// The storage of these locals are always live.
let mut ignored = StorageIgnored(IdxSetBuf::new_filled(mir.local_decls.len()));
ignored.visit_mir(mir);
let mut borrowed_locals = BorrowedLocals(IdxSetBuf::new_empty(mir.local_decls.len()));
borrowed_locals.visit_mir(mir);
// Calculate the MIR locals which have been previously
// borrowed (even if they are still active).
// This is only used for immovable generators.
let borrowed_locals = if !movable {
let analysis = HaveBeenBorrowedLocals::new(mir);
let result =
do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis,
|bd, p| DebugFormatted::new(&bd.mir().local_decls[p]));
Some((analysis, result))
} else {
None
};
// Calculate the liveness of MIR locals ignoring borrows.
let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
let mut liveness = liveness::liveness_of_locals(mir, LivenessMode {
include_regular_use: true,
......@@ -396,24 +413,41 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
statement_index: data.statements.len(),
};
let storage_liveness = state_for_location(loc, &analysis, &storage_live, mir);
if let Some((ref analysis, ref result)) = borrowed_locals {
let borrowed_locals = state_for_location(loc,
analysis,
result,
mir);
// The `liveness` variable contains the liveness of MIR locals ignoring borrows.
// This is correct for movable generators since borrows cannot live across
// suspension points. However for immovable generators we need to account for
// borrows, so we conseratively assume that all borrowed locals live forever.
// To do this we just union our `liveness` result with `borrowed_locals`, which
// contains all the locals which has been borrowed before this suspension point.
// If a borrow is converted to a raw reference, we must also assume that it lives
// forever. Note that the final liveness is still bounded by the storage liveness
// of the local, which happens using the `intersect` operation below.
liveness.outs[block].union(&borrowed_locals);
}
let mut storage_liveness = state_for_location(loc,
&storage_live_analysis,
&storage_live,
mir);
// Store the storage liveness for later use so we can restore the state
// after a suspension point
storage_liveness_map.insert(block, storage_liveness.clone());
let mut live_locals = storage_liveness;
// Mark locals without storage statements as always having live storage
live_locals.union(&ignored.0);
storage_liveness.union(&ignored.0);
if !movable {
// For immovable generators we consider borrowed locals to always be live.
// This effectively makes those locals use just the storage liveness.
liveness.outs[block].union(&borrowed_locals.0);
}
// Locals live are live at this point only if they are used across
// suspension points (the `liveness` variable)
// and their storage is live (the `storage_liveness` variable)
storage_liveness.intersect(&liveness.outs[block]);
// Locals live are live at this point only if they are used across suspension points
// and their storage is live
live_locals.intersect(&liveness.outs[block]);
let live_locals = storage_liveness;
// Add the locals life at this suspension point to the set of locals which live across
// any suspension points
......
......@@ -150,15 +150,15 @@ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
}
fn visit_pat(&mut self, pat: &'tcx Pat) {
intravisit::walk_pat(self, pat);
self.expr_count += 1;
if let PatKind::Binding(..) = pat.node {
let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
let ty = self.fcx.tables.borrow().pat_ty(pat);
self.record(ty, Some(scope), None, pat.span);
}
self.expr_count += 1;
intravisit::walk_pat(self, pat);
}
fn visit_expr(&mut self, expr: &'tcx Expr) {
......
......@@ -1015,7 +1015,9 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
let span = body.value.span;
if body.is_generator && can_be_generator.is_some() {
fcx.yield_ty = Some(fcx.next_ty_var(TypeVariableOrigin::TypeInference(span)));
let yield_ty = fcx.next_ty_var(TypeVariableOrigin::TypeInference(span));
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
fcx.yield_ty = Some(yield_ty);
}
GatherLocalsVisitor { fcx: &fcx, }.visit_body(body);
......
......@@ -112,6 +112,7 @@ fn ident_can_begin_expr(ident: ast::Ident) -> bool {
keywords::Unsafe.name(),
keywords::While.name(),
keywords::Yield.name(),
keywords::Static.name(),
].contains(&ident.name)
}
......
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(generators)]
fn main() {
unsafe {
static move || {
// Tests that the generator transformation finds out that `a` is not live
// during the yield expression. Type checking will also compute liveness
// and it should also find out that `a` is not live.
// The compiler will panic if the generator transformation finds that
// `a` is live and type checking finds it dead.
let a = {
yield ();
4i32
};
&a;
};
}
}
error[E0626]: borrow may still be in use when generator yields (Mir)
--> $DIR/generator-with-nll.rs:20:17
|
20 | let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
| ^^^^^^^^^
21 | //~^ borrow may still be in use when generator yields (Mir)
22 | yield ();
| -------- possible yield occurs here
error[E0626]: borrow may still be in use when generator yields (Ast)
--> $DIR/generator-with-nll.rs:19:23
|
......@@ -25,5 +16,14 @@ error[E0626]: borrow may still be in use when generator yields (Ast)
22 | yield ();
| -------- possible yield occurs here
error[E0626]: borrow may still be in use when generator yields (Mir)
--> $DIR/generator-with-nll.rs:20:17
|
20 | let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
| ^^^^^^^^^
21 | //~^ borrow may still be in use when generator yields (Mir)
22 | yield ();
| -------- possible yield occurs here
error: aborting due to 3 previous errors
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(generators)]
enum Test { A(i32), B, }
fn main() { }
fn fun(test: Test) {
move || {
if let Test::A(ref _a) = test { //~ ERROR borrow may still be in use when generator yields
yield ();
}
};
}
error[E0626]: borrow may still be in use when generator yields
--> $DIR/pattern-borrow.rs:19:24
|
19 | if let Test::A(ref _a) = test { //~ ERROR borrow may still be in use when generator yields
| ^^^^^^
20 | yield ();
| -------- possible yield occurs here
error: aborting due to previous error
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn main() {
let s = String::from("foo");
let mut gen = move || { //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
yield s[..];
};
gen.resume(); //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
}
error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
--> $DIR/sized-yield.rs:17:26
|
17 | let mut gen = move || { //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
| __________________________^
18 | | yield s[..];
19 | | };
| |____^ `str` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: the yield type of a generator must have a statically known size
error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
--> $DIR/sized-yield.rs:20:8
|
20 | gen.resume(); //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
| ^^^^^^ `str` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
error: aborting due to 2 previous errors
error[E0626]: borrow may still be in use when generator yields (Mir)
--> $DIR/yield-while-local-borrowed.rs:24:17
|
24 | let a = &mut 3;
| ^^^^^^
...
27 | yield();
| ------- possible yield occurs here
error[E0626]: borrow may still be in use when generator yields (Ast)
--> $DIR/yield-while-local-borrowed.rs:24:22
|
......@@ -25,6 +16,15 @@ error[E0626]: borrow may still be in use when generator yields (Ast)
55 | yield();
| ------- possible yield occurs here
error[E0626]: borrow may still be in use when generator yields (Mir)
--> $DIR/yield-while-local-borrowed.rs:24:17
|
24 | let a = &mut 3;
| ^^^^^^
...
27 | yield();
| ------- possible yield occurs here
error[E0626]: borrow may still be in use when generator yields (Mir)
--> $DIR/yield-while-local-borrowed.rs:52:21
|
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册