From ccf0d8399e1ef3ed6bf7005650ce42aa646b5cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 7 Oct 2017 16:36:28 +0200 Subject: [PATCH 1/6] Adds support for immovable generators. Move checking of invalid borrows across suspension points to borrowck. Fixes #44197, #45259 and #45093. --- src/librustc/diagnostics.rs | 2 + src/librustc/hir/lowering.rs | 26 +++-- src/librustc/hir/mod.rs | 8 +- src/librustc/ich/impls_hir.rs | 5 + src/librustc/ich/impls_ty.rs | 5 +- .../nice_region_error/outlives_closure.rs | 2 +- src/librustc/infer/freshen.rs | 1 + src/librustc/middle/region.rs | 88 ++++++++++++++- src/librustc/traits/coherence.rs | 5 +- src/librustc/traits/error_reporting.rs | 1 + src/librustc/traits/select.rs | 16 ++- src/librustc/ty/context.rs | 9 +- src/librustc/ty/error.rs | 1 + src/librustc/ty/fast_reject.rs | 6 ++ src/librustc/ty/flags.rs | 6 ++ src/librustc/ty/item_path.rs | 1 + src/librustc/ty/layout.rs | 7 +- src/librustc/ty/mod.rs | 7 +- src/librustc/ty/outlives.rs | 9 +- src/librustc/ty/relate.rs | 44 +++++++- src/librustc/ty/structural_impls.rs | 9 +- src/librustc/ty/sty.rs | 19 ++-- src/librustc/ty/util.rs | 16 ++- src/librustc/ty/walk.rs | 5 +- src/librustc/ty/wf.rs | 1 + src/librustc/util/ppaux.rs | 27 ++++- src/librustc_borrowck/borrowck/check_loans.rs | 101 +++++++++++++++++- src/librustc_borrowck/borrowck/mod.rs | 67 ------------ src/librustc_lint/types.rs | 1 + src/librustc_mir/interpret/const_eval.rs | 1 + src/librustc_mir/monomorphize/item.rs | 1 + src/librustc_mir/transform/generator.rs | 77 ++++++++++--- src/librustc_mir/util/pretty.rs | 20 ++-- src/librustc_save_analysis/dump_visitor.rs | 2 +- src/librustc_trans/debuginfo/type_names.rs | 1 + src/librustc_typeck/check/cast.rs | 2 +- src/librustc_typeck/check/closure.rs | 6 +- .../check/generator_interior.rs | 89 ++++++++++----- src/librustc_typeck/check/mod.rs | 38 ++++--- src/librustc_typeck/check/upvar.rs | 8 +- src/librustc_typeck/collect.rs | 4 +- src/librustc_typeck/diagnostics.rs | 1 + src/librustc_typeck/variance/constraints.rs | 1 + src/librustdoc/clean/mod.rs | 1 + src/libsyntax/ast.rs | 9 +- src/libsyntax/ext/build.rs | 7 +- src/libsyntax/fold.rs | 3 +- src/libsyntax/parse/parser.rs | 45 ++++++-- src/libsyntax/print/pprust.rs | 11 +- src/libsyntax/visit.rs | 2 +- src/test/compile-fail/static-closures.rs | 14 +++ .../pprust-expr-roundtrip.rs | 6 +- src/test/run-pass/generator/issue-44197.rs | 40 +++++++ .../generator/live-upvar-across-yield.rs | 21 ++++ .../run-pass/generator/nested_generators.rs | 30 ++++++ .../run-pass/generator/reborrow-mut-upvar.rs | 24 +++++ .../run-pass/generator/static-generators.rs | 24 +++++ src/test/ui/generator/auto-trait-regions.rs | 58 ++++++++++ .../ui/generator/auto-trait-regions.stderr | 35 ++++++ src/test/ui/generator/not-send-sync.stderr | 8 +- 60 files changed, 867 insertions(+), 217 deletions(-) create mode 100644 src/test/compile-fail/static-closures.rs create mode 100644 src/test/run-pass/generator/issue-44197.rs create mode 100644 src/test/run-pass/generator/live-upvar-across-yield.rs create mode 100644 src/test/run-pass/generator/nested_generators.rs create mode 100644 src/test/run-pass/generator/reborrow-mut-upvar.rs create mode 100644 src/test/run-pass/generator/static-generators.rs create mode 100644 src/test/ui/generator/auto-trait-regions.rs create mode 100644 src/test/ui/generator/auto-trait-regions.stderr diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 1d7cde8126547..8bd89b834d6b6 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2118,4 +2118,6 @@ register_diagnostics! { E0657, // `impl Trait` can only capture lifetimes bound at the fn level E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders + + E0906, // closures cannot be static } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 32b55a05124ac..919c69e2abb00 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2768,7 +2768,7 @@ impl<'a> LoweringContext<'a> { arms.iter().map(|x| self.lower_arm(x)).collect(), hir::MatchSource::Normal) } - ExprKind::Closure(capture_clause, ref decl, ref body, fn_decl_span) => { + ExprKind::Closure(capture_clause, movability, ref decl, ref body, fn_decl_span) => { self.with_new_scopes(|this| { this.with_parent_def(e.id, |this| { let mut is_generator = false; @@ -2777,16 +2777,28 @@ impl<'a> LoweringContext<'a> { is_generator = this.is_generator; e }); - if is_generator && !decl.inputs.is_empty() { - span_err!(this.sess, fn_decl_span, E0628, - "generators cannot have explicit arguments"); - this.sess.abort_if_errors(); - } + let generator_option = if is_generator { + if !decl.inputs.is_empty() { + span_err!(this.sess, fn_decl_span, E0628, + "generators cannot have explicit arguments"); + this.sess.abort_if_errors(); + } + Some(match movability { + Movability::Movable => hir::GeneratorMovability::Movable, + Movability::Static => hir::GeneratorMovability::Static, + }) + } else { + if movability == Movability::Static { + span_err!(this.sess, fn_decl_span, E0906, + "closures cannot be static"); + } + None + }; hir::ExprClosure(this.lower_capture_clause(capture_clause), this.lower_fn_decl(decl, None, false), body_id, fn_decl_span, - is_generator) + generator_option) }) }) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 3da2855929d3a..8921fecf1b886 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1290,7 +1290,7 @@ pub enum Expr_ { /// /// This may also be a generator literal, indicated by the final boolean, /// in that case there is an GeneratorClause. - ExprClosure(CaptureClause, P, BodyId, Span, bool), + ExprClosure(CaptureClause, P, BodyId, Span, Option), /// A block (`{ ... }`) ExprBlock(P), @@ -1466,6 +1466,12 @@ pub struct Destination { pub target_id: ScopeTarget, } +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +pub enum GeneratorMovability { + Static, + Movable, +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] pub enum CaptureClause { CaptureByValue, diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index cc1b028480e4d..59d9db48bdc63 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -606,6 +606,11 @@ impl<'gcx> HashStable> for hir::MatchSource { } } +impl_stable_hash_for!(enum hir::GeneratorMovability { + Static, + Movable +}); + impl_stable_hash_for!(enum hir::CaptureClause { CaptureByValue, CaptureByRef diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 4ae114c4e69da..107779ec3fa15 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -431,7 +431,7 @@ for ::middle::const_val::ErrKind<'gcx> { impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs }); -impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness }); +impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness, movable }); impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> { parent, @@ -656,6 +656,9 @@ for ty::TypeVariants<'gcx> closure_substs.hash_stable(hcx, hasher); interior.hash_stable(hcx, hasher); } + TyGeneratorWitness(types) => { + types.hash_stable(hcx, hasher) + } TyTuple(inner_tys, from_diverging_type_var) => { inner_tys.hash_stable(hcx, hasher); from_diverging_type_var.hash_stable(hcx, hasher); diff --git a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs index 95f44b813c5d2..18b8c70c3ef79 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs @@ -60,7 +60,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { if let Some(node_id) = hir.as_local_node_id(free_region.scope) { match hir.get(node_id) { NodeExpr(Expr { - node: ExprClosure(_, _, _, closure_span, false), + node: ExprClosure(_, _, _, closure_span, None), .. }) => { let sup_sp = sup_origin.span(); diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 1783d5abfc7c6..8b61fcff2335e 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -192,6 +192,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { ty::TyForeign(..) | ty::TyParam(..) | ty::TyClosure(..) | + ty::TyGeneratorWitness(..) | ty::TyAnon(..) => { t.super_fold_with(self) } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 049bf4470cbc6..66b3adb83c160 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -453,6 +453,43 @@ struct RegionResolutionVisitor<'a, 'tcx: 'a> { terminating_scopes: FxHashSet, } +struct ExprLocatorVisitor { + id: ast::NodeId, + result: Option, + expr_and_pat_count: usize, +} + +// This visitor has to have the same visit_expr calls as RegionResolutionVisitor +// since `expr_count` is compared against the results there. +impl<'tcx> Visitor<'tcx> for ExprLocatorVisitor { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::None + } + + fn visit_pat(&mut self, pat: &'tcx Pat) { + self.expr_and_pat_count += 1; + + intravisit::walk_pat(self, pat); + } + + fn visit_expr(&mut self, expr: &'tcx Expr) { + debug!("ExprLocatorVisitor - pre-increment {} expr = {:?}", + self.expr_and_pat_count, + expr); + + intravisit::walk_expr(self, expr); + + self.expr_and_pat_count += 1; + + debug!("ExprLocatorVisitor - post-increment {} expr = {:?}", + self.expr_and_pat_count, + expr); + + if expr.id == self.id { + self.result = Some(self.expr_and_pat_count); + } + } +} impl<'tcx> ScopeTree { pub fn record_scope_parent(&mut self, child: Scope, parent: Option) { @@ -612,6 +649,20 @@ impl<'tcx> ScopeTree { return true; } + /// Returns the id of the innermost containing body + pub fn containing_body(&self, mut scope: Scope)-> Option { + loop { + if let ScopeData::CallSite(id) = scope.data() { + return Some(id); + } + + match self.opt_encl_scope(scope) { + None => return None, + Some(parent) => scope = parent, + } + } + } + /// Finds the nearest common ancestor (if any) of two scopes. That is, finds the smallest /// scope which is greater than or equal to both `scope_a` and `scope_b`. pub fn nearest_common_ancestor(&self, @@ -768,6 +819,28 @@ impl<'tcx> ScopeTree { self.yield_in_scope.get(&scope).cloned() } + /// Checks whether the given scope contains a `yield` and if that yield could execute + /// after `expr`. If so, it returns the span of that `yield`. + /// `scope` must be inside the body. + pub fn yield_in_scope_for_expr(&self, + scope: Scope, + expr: ast::NodeId, + body: &'tcx hir::Body) -> Option { + self.yield_in_scope(scope).and_then(|(span, count)| { + let mut visitor = ExprLocatorVisitor { + id: expr, + result: None, + expr_and_pat_count: 0, + }; + visitor.visit_body(body); + if count >= visitor.result.unwrap() { + Some(span) + } else { + None + } + }) + } + /// Gives the number of expressions visited in a body. /// Used to sanity check visit_expr call count when /// calculating generator interiors. @@ -872,9 +945,13 @@ fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: & record_var_lifetime(visitor, pat.hir_id.local_id, pat.span); } + debug!("resolve_pat - pre-increment {} pat = {:?}", visitor.expr_and_pat_count, pat); + intravisit::walk_pat(visitor, pat); visitor.expr_and_pat_count += 1; + + debug!("resolve_pat - post-increment {} pat = {:?}", visitor.expr_and_pat_count, pat); } fn resolve_stmt<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, stmt: &'tcx hir::Stmt) { @@ -897,7 +974,7 @@ fn resolve_stmt<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, stmt: } fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr: &'tcx hir::Expr) { - debug!("resolve_expr(expr.id={:?})", expr.id); + debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); let prev_cx = visitor.cx; visitor.enter_node_scope_with_dtor(expr.hir_id.local_id); @@ -982,6 +1059,8 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr: visitor.expr_and_pat_count += 1; + debug!("resolve_expr post-increment {}, expr = {:?}", visitor.expr_and_pat_count, expr); + if let hir::ExprYield(..) = expr.node { // Mark this expr's scope and all parent scopes as containing `yield`. let mut scope = Scope::Node(expr.hir_id.local_id); @@ -1077,12 +1156,13 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, } } - if let Some(pat) = pat { - visitor.visit_pat(pat); - } + // Make sure we visit the initializer first, so expr_and_pat_count remains correct if let Some(expr) = init { visitor.visit_expr(expr); } + if let Some(pat) = pat { + visitor.visit_pat(pat); + } /// True if `pat` match the `P&` nonterminal: /// diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index 7d1f3b31bfc27..ae68e3fe8d01f 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -451,7 +451,10 @@ fn ty_is_local_constructor(ty: Ty, in_crate: InCrate) -> bool { true } - ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAnon(..) => { + ty::TyClosure(..) | + ty::TyGenerator(..) | + ty::TyGeneratorWitness(..) | + ty::TyAnon(..) => { bug!("ty_is_local invoked on unexpected type: {:?}", ty) } } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e649f1b49df76..067340ecacc00 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -262,6 +262,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }, ty::TyGenerator(..) => Some(18), ty::TyForeign(..) => Some(19), + ty::TyGeneratorWitness(..) => Some(20), ty::TyInfer(..) | ty::TyError => None } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 51d2bc8701a4e..55cbc890e1e91 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -2044,8 +2044,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) | ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) | ty::TyChar | ty::TyRef(..) | ty::TyGenerator(..) | - ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever | - ty::TyError => { + ty::TyGeneratorWitness(..) | ty::TyArray(..) | ty::TyClosure(..) | + ty::TyNever | ty::TyError => { // safe for everything Where(ty::Binder(Vec::new())) } @@ -2095,7 +2095,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) | - ty::TyGenerator(..) | ty::TyForeign(..) | + ty::TyGenerator(..) | ty::TyGeneratorWitness(..) | ty::TyForeign(..) | ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => { Never } @@ -2206,8 +2206,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } ty::TyGenerator(def_id, ref substs, interior) => { - let witness = iter::once(interior.witness); - substs.upvar_tys(def_id, self.tcx()).chain(witness).collect() + substs.upvar_tys(def_id, self.tcx()).chain(iter::once(interior.witness)).collect() + } + + ty::TyGeneratorWitness(types) => { + // This is sound because no regions in the witness can refer to + // the binder outside the witness. So we'll effectivly reuse + // the implicit binder around the witness. + types.skip_binder().to_vec() } // for `PhantomData`, we pass `T` diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 87742fe91627e..e4e07454c97ac 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1672,8 +1672,9 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { pub fn print_debug_stats(self) { sty_debug_print!( self, - TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyGenerator, TyForeign, - TyDynamic, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon); + TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, + TyGenerator, TyGeneratorWitness, TyDynamic, TyClosure, TyTuple, + TyParam, TyInfer, TyProjection, TyAnon, TyForeign); println!("Substs interner: #{}", self.interners.substs.borrow().len()); println!("Region interner: #{}", self.interners.region.borrow().len()); @@ -2079,6 +2080,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_ty(TyGenerator(id, closure_substs, interior)) } + pub fn mk_generator_witness(self, types: ty::Binder<&'tcx Slice>>) -> Ty<'tcx> { + self.mk_ty(TyGeneratorWitness(types)) + } + pub fn mk_var(self, v: TyVid) -> Ty<'tcx> { self.mk_infer(TyVar(v)) } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index d2152024cff07..583612f9590f1 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -227,6 +227,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { } ty::TyClosure(..) => "closure".to_string(), ty::TyGenerator(..) => "generator".to_string(), + ty::TyGeneratorWitness(..) => "generator witness".to_string(), ty::TyTuple(..) => "tuple".to_string(), ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(), ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(), diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 138f6af77c658..97c259e6bf383 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -46,6 +46,7 @@ pub enum SimplifiedTypeGen TraitSimplifiedType(D), ClosureSimplifiedType(D), GeneratorSimplifiedType(D), + GeneratorWitnessSimplifiedType(usize), AnonSimplifiedType(D), FunctionSimplifiedType(usize), ParameterSimplifiedType, @@ -92,6 +93,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty::TyGenerator(def_id, _, _) => { Some(GeneratorSimplifiedType(def_id)) } + ty::TyGeneratorWitness(ref tys) => { + Some(GeneratorWitnessSimplifiedType(tys.skip_binder().len())) + } ty::TyNever => Some(NeverSimplifiedType), ty::TyTuple(ref tys, _) => { Some(TupleSimplifiedType(tys.len())) @@ -141,6 +145,7 @@ impl SimplifiedTypeGen { TraitSimplifiedType(d) => TraitSimplifiedType(map(d)), ClosureSimplifiedType(d) => ClosureSimplifiedType(map(d)), GeneratorSimplifiedType(d) => GeneratorSimplifiedType(map(d)), + GeneratorWitnessSimplifiedType(n) => GeneratorWitnessSimplifiedType(n), AnonSimplifiedType(d) => AnonSimplifiedType(map(d)), FunctionSimplifiedType(n) => FunctionSimplifiedType(n), ParameterSimplifiedType => ParameterSimplifiedType, @@ -175,6 +180,7 @@ impl<'gcx, D> HashStable> for SimplifiedTypeGen TraitSimplifiedType(d) => d.hash_stable(hcx, hasher), ClosureSimplifiedType(d) => d.hash_stable(hcx, hasher), GeneratorSimplifiedType(d) => d.hash_stable(hcx, hasher), + GeneratorWitnessSimplifiedType(n) => n.hash_stable(hcx, hasher), AnonSimplifiedType(d) => d.hash_stable(hcx, hasher), FunctionSimplifiedType(n) => n.hash_stable(hcx, hasher), ForeignSimplifiedType(d) => d.hash_stable(hcx, hasher), diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 63c646dbd2310..2889322a1ce77 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -94,6 +94,12 @@ impl FlagComputation { self.add_ty(interior.witness); } + &ty::TyGeneratorWitness(ref ts) => { + let mut computation = FlagComputation::new(); + computation.add_tys(&ts.skip_binder()[..]); + self.add_bound_computation(&computation); + } + &ty::TyClosure(_, ref substs) => { self.add_flags(TypeFlags::HAS_TY_CLOSURE); self.add_flags(TypeFlags::HAS_LOCAL_NAMES); diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 0c920a6f13e59..fefd11eecf4fa 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -375,6 +375,7 @@ pub fn characteristic_def_id_of_type(ty: Ty) -> Option { ty::TyAnon(..) | ty::TyInfer(_) | ty::TyError | + ty::TyGeneratorWitness(..) | ty::TyNever | ty::TyFloat(_) => None, } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 50efb73003731..69d07eafdca7a 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1678,7 +1678,7 @@ impl<'a, 'tcx> LayoutDetails { ty::TyParam(_) => { return Err(LayoutError::Unknown(ty)); } - ty::TyInfer(_) | ty::TyError => { + ty::TyGeneratorWitness(..) | ty::TyInfer(_) | ty::TyError => { bug!("LayoutDetails::compute: unexpected type `{}`", ty) } }) @@ -2151,8 +2151,9 @@ impl<'a, 'tcx> TyLayout<'tcx> { ty::TyFnPtr(_) | ty::TyNever | ty::TyFnDef(..) | - ty::TyDynamic(..) | - ty::TyForeign(..) => { + ty::TyGeneratorWitness(..) | + ty::TyForeign(..) | + ty::TyDynamic(..) => { bug!("TyLayout::field_type({:?}): not applicable", self) } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 123432074761b..b3acfb7fac8a3 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1880,7 +1880,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { vec![] } - TyStr | TyDynamic(..) | TySlice(_) | TyForeign(..) | TyError => { + TyStr | + TyDynamic(..) | + TySlice(_) | + TyForeign(..) | + TyError | + TyGeneratorWitness(..) => { // these are never sized - return the target type vec![ty] } diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs index 707137649d771..ff99a4b7ff638 100644 --- a/src/librustc/ty/outlives.rs +++ b/src/librustc/ty/outlives.rs @@ -79,16 +79,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - ty::TyGenerator(def_id, ref substs, ref interior) => { + ty::TyGenerator(def_id, ref substs, _) => { // Same as the closure case for upvar_ty in substs.upvar_tys(def_id, *self) { self.compute_components(upvar_ty, out); } - // But generators can have additional interior types - self.compute_components(interior.witness, out); + // We ignore regions in the generator interior as we don't + // want these to affect region inference } + // All regions are bound inside a witness + ty::TyGeneratorWitness(..) => (), + // OutlivesTypeParameterEnv -- the actual checking that `X:'a` // is implied by the environment is done in regionck. ty::TyParam(p) => { diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 376cdc462e82f..a6c72728a5125 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -18,6 +18,7 @@ use middle::const_val::ConstVal; use traits::Reveal; use ty::subst::{Kind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; +use ty::fold::{TypeVisitor, TypeFolder}; use ty::error::{ExpectedFound, TypeError}; use util::common::ErrorReported; use std::rc::Rc; @@ -319,6 +320,33 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { } } +#[derive(Debug, Clone)] +struct GeneratorWitness<'tcx>(&'tcx ty::Slice>); + +impl<'tcx> TypeFoldable<'tcx> for GeneratorWitness<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + GeneratorWitness(self.0.fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) + } +} + +impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> { + fn relate<'a, 'gcx, R>(relation: &mut R, + a: &GeneratorWitness<'tcx>, + b: &GeneratorWitness<'tcx>) + -> RelateResult<'tcx, GeneratorWitness<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a + { + assert!(a.0.len() == b.0.len()); + let tcx = relation.tcx(); + let types = tcx.mk_type_list(a.0.iter().zip(b.0).map(|(a, b)| relation.relate(a, b)))?; + Ok(GeneratorWitness(types)) + } +} + impl<'tcx> Relate<'tcx> for Ty<'tcx> { fn relate<'a, 'gcx, R>(relation: &mut R, a: &Ty<'tcx>, @@ -410,6 +438,17 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, Ok(tcx.mk_generator(a_id, substs, interior)) } + (&ty::TyGeneratorWitness(a_types), &ty::TyGeneratorWitness(b_types)) => + { + // Wrap our types with a temporary GeneratorWitness struct + // inside the binder so we can related them + let a_types = ty::Binder(GeneratorWitness(*a_types.skip_binder())); + let b_types = ty::Binder(GeneratorWitness(*b_types.skip_binder())); + // Then remove the GeneratorWitness for the result + let types = ty::Binder(relation.relate(&a_types, &b_types)?.skip_binder().0); + Ok(tcx.mk_generator_witness(types)) + } + (&ty::TyClosure(a_id, a_substs), &ty::TyClosure(b_id, b_substs)) if a_id == b_id => @@ -575,8 +614,9 @@ impl<'tcx> Relate<'tcx> for ty::GeneratorInterior<'tcx> { -> RelateResult<'tcx, ty::GeneratorInterior<'tcx>> where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a { - let interior = relation.relate(&a.witness, &b.witness)?; - Ok(ty::GeneratorInterior::new(interior)) + assert_eq!(a.movable, b.movable); + let witness = relation.relate(&a.witness, &b.witness)?; + Ok(ty::GeneratorInterior { witness, movable: a.movable }) } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 438511281ba47..0dc1338fff860 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -438,7 +438,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorInterior<'a> { type Lifted = ty::GeneratorInterior<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { tcx.lift(&self.witness).map(|witness| { - ty::GeneratorInterior { witness } + ty::GeneratorInterior { witness, movable: self.movable } }) } } @@ -798,6 +798,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::TyGenerator(did, substs, interior) => { ty::TyGenerator(did, substs.fold_with(folder), interior.fold_with(folder)) } + ty::TyGeneratorWitness(types) => ty::TyGeneratorWitness(types.fold_with(folder)), ty::TyClosure(did, substs) => ty::TyClosure(did, substs.fold_with(folder)), ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)), ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)), @@ -832,6 +833,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::TyGenerator(_did, ref substs, ref interior) => { substs.visit_with(visitor) || interior.visit_with(visitor) } + ty::TyGeneratorWitness(ref types) => types.visit_with(visitor), ty::TyClosure(_did, ref substs) => substs.visit_with(visitor), ty::TyProjection(ref data) => data.visit_with(visitor), ty::TyAnon(_, ref substs) => substs.visit_with(visitor), @@ -928,7 +930,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::GeneratorInterior::new(self.witness.fold_with(folder)) + ty::GeneratorInterior { + witness: self.witness.fold_with(folder), + movable: self.movable, + } } fn super_visit_with>(&self, visitor: &mut V) -> bool { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 0889efdc142b1..b6ba7896497b4 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -141,6 +141,10 @@ pub enum TypeVariants<'tcx> { /// `|a| yield a`. TyGenerator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>), + /// A type representin the types stored inside a generator. + /// This should only appear in GeneratorInteriors. + TyGeneratorWitness(Binder<&'tcx Slice>>), + /// The never type `!` TyNever, @@ -405,19 +409,7 @@ impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct GeneratorInterior<'tcx> { pub witness: Ty<'tcx>, -} - -impl<'tcx> GeneratorInterior<'tcx> { - pub fn new(witness: Ty<'tcx>) -> GeneratorInterior<'tcx> { - GeneratorInterior { witness } - } - - pub fn as_slice(&self) -> &'tcx Slice> { - match self.witness.sty { - ty::TyTuple(s, _) => s, - _ => bug!(), - } - } + pub movable: bool, } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] @@ -1611,6 +1603,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } TyFnDef(..) | TyFnPtr(_) | + TyGeneratorWitness(..) | TyBool | TyChar | TyInt(_) | diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index de96e9dc8ff2d..34f05232adcab 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -29,7 +29,6 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, HashStable}; use rustc_data_structures::fx::FxHashMap; use std::cmp; -use std::iter; use std::hash::Hash; use std::intrinsics; use syntax::ast::{self, Name}; @@ -550,7 +549,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let result = match ty.sty { ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyStr | ty::TyNever | ty::TyForeign(..) | - ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => { + ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) | + ty::TyGeneratorWitness(..) => { // these types never have a destructor Ok(ty::DtorckConstraint::empty()) } @@ -572,8 +572,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }).collect() } - ty::TyGenerator(def_id, substs, interior) => { - substs.upvar_tys(def_id, self).chain(iter::once(interior.witness)).map(|ty| { + ty::TyGenerator(def_id, substs, _) => { + // Note that the interior types are ignored here. + // Any type reachable inside the interior must also be reachable + // through the upvars. + substs.upvar_tys(def_id, self).map(|ty| { self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) }).collect() } @@ -783,6 +786,9 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> self.def_id(d); } } + TyGeneratorWitness(tys) => { + self.hash(tys.skip_binder().len()); + } TyTuple(tys, defaulted) => { self.hash(tys.len()); self.hash(defaulted); @@ -1139,7 +1145,7 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Fast-path for primitive types ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) | ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever | - ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | + ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | ty::TyGeneratorWitness(..) | ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyStr => false, // Foreign types can never have destructors diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 448ad4cf675c7..45f0ad1cf1a5f 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -119,8 +119,11 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { stack.extend(substs.substs.types().rev()); } ty::TyGenerator(_, ref substs, ref interior) => { - stack.extend(substs.substs.types().rev()); stack.push(interior.witness); + stack.extend(substs.substs.types().rev()); + } + ty::TyGeneratorWitness(ts) => { + stack.extend(ts.skip_binder().iter().cloned().rev()); } ty::TyTuple(ts, _) => { stack.extend(ts.iter().cloned().rev()); diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index a851ccc34bfd6..ce44448ef794e 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -283,6 +283,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { ty::TyFloat(..) | ty::TyError | ty::TyStr | + ty::TyGeneratorWitness(..) | ty::TyNever | ty::TyParam(_) | ty::TyForeign(..) => { diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 51841836698eb..37d1c568515b5 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -17,7 +17,7 @@ use ty::{BrAnon, BrEnv, BrFresh, BrNamed}; use ty::{TyBool, TyChar, TyAdt}; use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr}; use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple}; -use ty::{TyClosure, TyGenerator, TyForeign, TyProjection, TyAnon}; +use ty::{TyClosure, TyGenerator, TyGeneratorWitness, TyForeign, TyProjection, TyAnon}; use ty::{TyDynamic, TyInt, TyUint, TyInfer}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use util::nodemap::FxHashSet; @@ -631,6 +631,22 @@ impl<'tcx> fmt::Debug for ty::UpvarBorrow<'tcx> { } } +define_print! { + ('tcx) &'tcx ty::Slice>, (self, f, cx) { + display { + write!(f, "{{")?; + let mut tys = self.iter(); + if let Some(&ty) = tys.next() { + print!(f, cx, print(ty))?; + for &ty in tys { + print!(f, cx, write(", "), print(ty))?; + } + } + write!(f, "}}") + } + } +} + define_print! { ('tcx) ty::TypeAndMut<'tcx>, (self, f, cx) { display { @@ -1066,7 +1082,11 @@ define_print! { TyStr => write!(f, "str"), TyGenerator(did, substs, interior) => ty::tls::with(|tcx| { let upvar_tys = substs.upvar_tys(did, tcx); - write!(f, "[generator")?; + if interior.movable { + write!(f, "[generator")?; + } else { + write!(f, "[static generator")?; + } if let Some(node_id) = tcx.hir.as_local_node_id(did) { write!(f, "@{:?}", tcx.hir.span(node_id))?; @@ -1097,6 +1117,9 @@ define_print! { print!(f, cx, write(" "), print(interior), write("]")) }), + TyGeneratorWitness(types) => { + ty::tls::with(|tcx| cx.in_binder(f, tcx, &types, tcx.lift(&types))) + } TyClosure(did, substs) => ty::tls::with(|tcx| { let upvar_tys = substs.upvar_tys(did, tcx); write!(f, "[closure")?; diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 908737669c5cb..7252a1795395b 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -25,7 +25,7 @@ use rustc::middle::expr_use_visitor::MutateMode; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::middle::region; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, TyCtxt, RegionKind}; use syntax::ast; use syntax_pos::Span; use rustc::hir; @@ -92,6 +92,7 @@ struct CheckLoanCtxt<'a, 'tcx: 'a> { move_data: &'a move_data::FlowedMoveData<'a, 'tcx>, all_loans: &'a [Loan<'tcx>], param_env: ty::ParamEnv<'tcx>, + movable_generator: bool, } impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> { @@ -147,6 +148,8 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> { } self.check_for_conflicting_loans(hir_id.local_id); + + self.check_for_loans_across_yields(cmt, loan_region, borrow_span); } fn mutate(&mut self, @@ -198,6 +201,16 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, debug!("check_loans(body id={})", body.value.id); let def_id = bccx.tcx.hir.body_owner_def_id(body.id()); + + let node_id = bccx.tcx.hir.as_local_node_id(def_id).unwrap(); + let movable_generator = !match bccx.tcx.hir.get(node_id) { + hir::map::Node::NodeExpr(&hir::Expr { + node: hir::ExprClosure(.., Some(hir::GeneratorMovability::Static)), + .. + }) => true, + _ => false, + }; + let param_env = bccx.tcx.param_env(def_id); let mut clcx = CheckLoanCtxt { bccx, @@ -205,6 +218,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, move_data, all_loans, param_env, + movable_generator, }; let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id); euv::ExprUseVisitor::new(&mut clcx, @@ -348,6 +362,91 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { return result; } + pub fn check_for_loans_across_yields(&self, + cmt: mc::cmt<'tcx>, + loan_region: ty::Region<'tcx>, + borrow_span: Span) { + pub fn borrow_of_local_data<'tcx>(cmt: &mc::cmt<'tcx>) -> bool { + match cmt.cat { + // Borrows of static items is allowed + Categorization::StaticItem => false, + // Reborrow of already borrowed data is ignored + // Any errors will be caught on the initial borrow + Categorization::Deref(..) => false, + + // By-ref upvars has Derefs so they will get ignored. + // Generators counts as FnOnce so this leaves only + // by-move upvars, which is local data for generators + Categorization::Upvar(..) => true, + + Categorization::Rvalue(region) => { + // Rvalues promoted to 'static are no longer local + if let RegionKind::ReStatic = *region { + false + } else { + true + } + } + + // Borrow of local data must be checked + Categorization::Local(..) => true, + + // For interior references and downcasts, find out if the base is local + Categorization::Downcast(ref cmt_base, _) | + Categorization::Interior(ref cmt_base, _) => borrow_of_local_data(&cmt_base), + } + } + + if !self.movable_generator { + return; + } + + if !borrow_of_local_data(&cmt) { + return; + } + + let scope = match *loan_region { + // A concrete region in which we will look for a yield expression + RegionKind::ReScope(scope) => scope, + + // There cannot be yields inside an empty region + RegionKind::ReEmpty => return, + + // Local data cannot have these lifetimes + RegionKind::ReEarlyBound(..) | + RegionKind::ReLateBound(..) | + RegionKind::ReFree(..) | + RegionKind::ReStatic => return, + + // These cannot exist in borrowck + RegionKind::ReVar(..) | + RegionKind::ReSkolemized(..) | + RegionKind::ReClosureBound(..) | + RegionKind::ReErased => span_bug!(borrow_span, + "unexpected region in borrowck {:?}", + loan_region), + }; + + let body_id = self.bccx.body.value.hir_id.local_id; + + if self.bccx.region_scope_tree.containing_body(scope) != Some(body_id) { + // We are borrowing a local data longer than it's storage. + // This should result in other borrowck errors. + // FIXME: Ensure an error is generated + return; + } + + if let Some(yield_span) = self.bccx + .region_scope_tree + .yield_in_scope_for_expr(scope, + cmt.id, + self.bccx.body) { + self.bccx.cannot_borrow_across_generator_yield(borrow_span, + yield_span, + Origin::Ast).emit(); + } + } + pub fn check_for_conflicting_loans(&self, node: hir::ItemLocalId) { //! Checks to see whether any of the loans that are issued //! on entrance to `node` conflict with loans that have already been diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index d003ef74f054b..f755efc89a58e 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -906,73 +906,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } }; - // When you have a borrow that lives across a yield, - // that reference winds up captured in the generator - // type. Regionck then constraints it to live as long - // as the generator itself. If that borrow is borrowing - // data owned by the generator, this winds up resulting in - // an `err_out_of_scope` error: - // - // ``` - // { - // let g = || { - // let a = &3; // this borrow is forced to ... -+ - // yield (); // | - // println!("{}", a); // | - // }; // | - // } <----------------------... live until here --------+ - // ``` - // - // To detect this case, we look for cases where the - // `super_scope` (lifetime of the value) is within the - // body, but the `sub_scope` is not. - debug!("err_out_of_scope: self.body.is_generator = {:?}", - self.body.is_generator); - let maybe_borrow_across_yield = if self.body.is_generator { - let body_scope = region::Scope::Node(self.body.value.hir_id.local_id); - debug!("err_out_of_scope: body_scope = {:?}", body_scope); - debug!("err_out_of_scope: super_scope = {:?}", super_scope); - debug!("err_out_of_scope: sub_scope = {:?}", sub_scope); - match (super_scope, sub_scope) { - (&ty::RegionKind::ReScope(value_scope), - &ty::RegionKind::ReScope(loan_scope)) => { - if { - // value_scope <= body_scope && - self.region_scope_tree.is_subscope_of(value_scope, body_scope) && - // body_scope <= loan_scope - self.region_scope_tree.is_subscope_of(body_scope, loan_scope) - } { - // We now know that this is a case - // that fits the bill described above: - // a borrow of something whose scope - // is within the generator, but the - // borrow is for a scope outside the - // generator. - // - // Now look within the scope of the of - // the value being borrowed (in the - // example above, that would be the - // block remainder that starts with - // `let a`) for a yield. We can cite - // that for the user. - self.region_scope_tree.yield_in_scope(value_scope) - } else { - None - } - } - _ => None, - } - } else { - None - }; - - if let Some((yield_span, _)) = maybe_borrow_across_yield { - debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span); - self.cannot_borrow_across_generator_yield(error_span, yield_span, Origin::Ast) - .emit(); - return; - } - let mut db = self.path_does_not_live_long_enough(error_span, &msg, Origin::Ast); let value_kind = match err.cmt.cat { mc::Categorization::Rvalue(..) => "temporary value", diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 2267d2b89ccf1..e0999db6e3e66 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -639,6 +639,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::TyError | ty::TyClosure(..) | ty::TyGenerator(..) | + ty::TyGeneratorWitness(..) | ty::TyProjection(..) | ty::TyAnon(..) | ty::TyFnDef(..) => bug!("Unexpected type in foreign function"), diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 2b95449f767dd..2913f72460e3e 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -514,6 +514,7 @@ fn check_ctfe_against_miri<'a, 'tcx>( TyDynamic(..) => bug!("miri produced a trait object"), TyClosure(..) => bug!("miri produced a closure"), TyGenerator(..) => bug!("miri produced a generator"), + TyGeneratorWitness(..) => bug!("miri produced a generator witness"), TyNever => bug!("miri produced a value of the never type"), TyProjection(_) => bug!("miri produced a projection"), TyAnon(..) => bug!("miri produced an impl Trait type"), diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 072c339813490..86a4dd4a31f8c 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -424,6 +424,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { ty::TyInfer(_) | ty::TyProjection(..) | ty::TyParam(_) | + ty::TyGeneratorWitness(_) | ty::TyAnon(..) => { bug!("DefPathBasedNames: Trying to create type name for \ unexpected type: {:?}", t); diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 9734edbe193c1..ebd34f81deb29 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -332,9 +332,39 @@ impl<'tcx> Visitor<'tcx> for StorageIgnored { } } +struct BorrowedLocals(liveness::LocalSet); + +fn mark_as_borrowed<'tcx>(place: &Place<'tcx>, locals: &mut BorrowedLocals) { + match *place { + Place::Local(l) => { locals.0.add(&l); }, + Place::Static(..) => (), + Place::Projection(ref proj) => { + match proj.elem { + // For derefs we don't look any further. + // If it pointed to a Local, it would already be borrowed elsewhere + ProjectionElem::Deref => (), + _ => mark_as_borrowed(&proj.base, locals) + } + } + } +} + +impl<'tcx> Visitor<'tcx> for BorrowedLocals { + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + if let Rvalue::Ref(_, _, ref place) = *rvalue { + mark_as_borrowed(place, self); + } + + self.super_rvalue(rvalue, location) + } +} + fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - source: MirSource) -> + source: MirSource, + movable: bool) -> (liveness::LocalSet, HashMap) { let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); @@ -347,8 +377,11 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, 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); + let mut set = liveness::LocalSet::new_empty(mir.local_decls.len()); - let liveness = liveness::liveness_of_locals(mir, LivenessMode { + let mut liveness = liveness::liveness_of_locals(mir, LivenessMode { include_regular_use: true, include_drops: true, }); @@ -372,6 +405,12 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Mark locals without storage statements as always having live storage live_locals.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 // and their storage is live live_locals.intersect(&liveness.outs[block]); @@ -390,6 +429,7 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, + upvars: Vec>, interior: GeneratorInterior<'tcx>, mir: &mut Mir<'tcx>) -> (HashMap, usize)>, @@ -397,11 +437,17 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, HashMap) { // Use a liveness analysis to compute locals which are live across a suspension point - let (live_locals, storage_liveness) = locals_live_across_suspend_points(tcx, mir, source); - + let (live_locals, storage_liveness) = locals_live_across_suspend_points(tcx, + mir, + source, + interior.movable); // Erase regions from the types passed in from typeck so we can compare them with // MIR types - let allowed = tcx.erase_regions(&interior.as_slice()); + let allowed_upvars = tcx.erase_regions(&upvars); + let allowed = match interior.witness.sty { + ty::TyGeneratorWitness(s) => tcx.erase_late_bound_regions(&s), + _ => bug!(), + }; for (local, decl) in mir.local_decls.iter_enumerated() { // Ignore locals which are internal or not live @@ -411,7 +457,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Sanity check that typeck knows about the type of locals which are // live across a suspension point - if !allowed.contains(&decl.ty) { + if !allowed.contains(&decl.ty) && !allowed_upvars.contains(&decl.ty) { span_bug!(mir.span, "Broken MIR: generator contains type {} in MIR, \ but typeck only knows about {}", @@ -763,19 +809,18 @@ impl MirPass for StateTransform { assert!(mir.generator_drop.is_none()); let def_id = source.def_id; - let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); - let hir_id = tcx.hir.node_to_hir_id(node_id); - - // Get the interior types which typeck computed - let tables = tcx.typeck_tables_of(def_id); - let interior = match tables.node_id_to_type(hir_id).sty { - ty::TyGenerator(_, _, interior) => interior, - ref t => bug!("type of generator not a generator: {:?}", t), - }; // The first argument is the generator type passed by value let gen_ty = mir.local_decls.raw[1].ty; + // Get the interior types and substs which typeck computed + let (upvars, interior) = match gen_ty.sty { + ty::TyGenerator(_, substs, interior) => { + (substs.upvar_tys(def_id, tcx).collect(), interior) + } + _ => bug!(), + }; + // Compute GeneratorState let state_did = tcx.lang_items().gen_state().unwrap(); let state_adt_ref = tcx.adt_def(state_did); @@ -790,7 +835,7 @@ impl MirPass for StateTransform { // Extract locals which are live across suspension point into `layout` // `remap` gives a mapping from local indices onto generator struct indices // `storage_liveness` tells us which locals have live storage at suspension points - let (remap, layout, storage_liveness) = compute_layout(tcx, source, interior, mir); + let (remap, layout, storage_liveness) = compute_layout(tcx, source, upvars, interior, mir); let state_field = mir.upvar_decls.len(); diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 6ab5fee79c661..f326b6e9274fc 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -397,17 +397,17 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> { self.super_constant(constant, location); let Constant { span, ty, literal } = constant; self.push(&format!("mir::Constant")); - self.push(&format!("└ span: {:?}", span)); - self.push(&format!("└ ty: {:?}", ty)); - self.push(&format!("└ literal: {:?}", literal)); + self.push(&format!("+ span: {:?}", span)); + self.push(&format!("+ ty: {:?}", ty)); + self.push(&format!("+ literal: {:?}", literal)); } fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { self.super_const(constant); let ty::Const { ty, val } = constant; self.push(&format!("ty::Const")); - self.push(&format!("└ ty: {:?}", ty)); - self.push(&format!("└ val: {:?}", val)); + self.push(&format!("+ ty: {:?}", ty)); + self.push(&format!("+ val: {:?}", val)); } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { @@ -416,15 +416,15 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> { Rvalue::Aggregate(kind, _) => match **kind { AggregateKind::Closure(def_id, substs) => { self.push(&format!("closure")); - self.push(&format!("└ def_id: {:?}", def_id)); - self.push(&format!("└ substs: {:#?}", substs)); + self.push(&format!("+ def_id: {:?}", def_id)); + self.push(&format!("+ substs: {:#?}", substs)); } AggregateKind::Generator(def_id, substs, interior) => { self.push(&format!("generator")); - self.push(&format!("└ def_id: {:?}", def_id)); - self.push(&format!("└ substs: {:#?}", substs)); - self.push(&format!("└ interior: {:?}", interior)); + self.push(&format!("+ def_id: {:?}", def_id)); + self.push(&format!("+ substs: {:#?}", substs)); + self.push(&format!("+ interior: {:?}", interior)); } _ => {} diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 30e3c9c4ca8a3..69cef20622b1e 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1602,7 +1602,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc _ => span_bug!(ex.span, "Expected struct or tuple type, found {:?}", ty), } } - ast::ExprKind::Closure(_, ref decl, ref body, _fn_decl_span) => { + ast::ExprKind::Closure(_, _, ref decl, ref body, _fn_decl_span) => { let mut id = String::from("$"); id.push_str(&ex.id.to_string()); diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index 0aec92b0d66cf..6490d109f2936 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -173,6 +173,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ty::TyInfer(_) | ty::TyProjection(..) | ty::TyAnon(..) | + ty::TyGeneratorWitness(..) | ty::TyParam(_) => { bug!("debuginfo: Trying to create type name for \ unexpected type: {:?}", t); diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index d2f759f5d99a4..992a510f71311 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -130,7 +130,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty::TyInfer(_) => None, ty::TyBool | ty::TyChar | ty::TyInt(..) | ty::TyUint(..) | - ty::TyFloat(_) | ty::TyArray(..) | + ty::TyFloat(_) | ty::TyArray(..) | ty::TyGeneratorWitness(..) | ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(..) | ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAdt(..) | ty::TyNever | ty::TyError => { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 147347a75abe8..df15f781ae8c9 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -37,6 +37,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _capture: hir::CaptureClause, decl: &'gcx hir::FnDecl, body_id: hir::BodyId, + gen: Option, expected: Expectation<'tcx>, ) -> Ty<'tcx> { debug!( @@ -53,7 +54,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { None => (None, None), }; let body = self.tcx.hir.body(body_id); - self.check_closure(expr, expected_kind, decl, body, expected_sig) + self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) } fn check_closure( @@ -62,6 +63,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { opt_kind: Option, decl: &'gcx hir::FnDecl, body: &'gcx hir::Body, + gen: Option, expected_sig: Option>, ) -> Ty<'tcx> { debug!( @@ -86,7 +88,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { decl, expr.id, body, - true, + gen, ).1; // Create type variables (for now) to represent the transformed diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs index af1297697c241..781eeaef2482c 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/src/librustc_typeck/check/generator_interior.rs @@ -17,8 +17,9 @@ use rustc::hir::def_id::DefId; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, Pat, PatKind, Expr}; use rustc::middle::region; -use rustc::ty::Ty; +use rustc::ty::{self, Ty, GeneratorInterior}; use std::rc::Rc; +use syntax_pos::Span; use super::FnCtxt; use util::nodemap::FxHashMap; @@ -30,36 +31,53 @@ struct InteriorVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> { - fn record(&mut self, ty: Ty<'tcx>, scope: Option, expr: Option<&'tcx Expr>) { + fn record(&mut self, + ty: Ty<'tcx>, + scope: Option, + expr: Option<&'tcx Expr>, + source_span: Span) { use syntax_pos::DUMMY_SP; let live_across_yield = scope.map_or(Some(DUMMY_SP), |s| { - self.region_scope_tree.yield_in_scope(s).and_then(|(span, expr_count)| { + self.region_scope_tree.yield_in_scope(s).and_then(|(yield_span, expr_count)| { // If we are recording an expression that is the last yield // in the scope, or that has a postorder CFG index larger // than the one of all of the yields, then its value can't // be storage-live (and therefore live) at any of the yields. // // See the mega-comment at `yield_in_scope` for a proof. + + debug!("comparing counts yield: {} self: {}, source_span = {:?}", + expr_count, self.expr_count, source_span); + if expr_count >= self.expr_count { - Some(span) + Some(yield_span) } else { None } }) }); - if let Some(span) = live_across_yield { + if let Some(yield_span) = live_across_yield { let ty = self.fcx.resolve_type_vars_if_possible(&ty); - debug!("type in expr = {:?}, scope = {:?}, type = {:?}, span = {:?}", - expr, scope, ty, span); - - // Map the type to the number of types added before it - let entries = self.types.len(); - self.types.entry(&ty).or_insert(entries); + debug!("type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", + expr, scope, ty, self.expr_count, yield_span); + + if self.fcx.any_unresolved_type_vars(&ty) { + let mut err = struct_span_err!(self.fcx.tcx.sess, source_span, E0907, + "type inside generator must be known in this context"); + err.span_note(yield_span, + "the type is part of the generator because of this `yield`"); + err.emit(); + } else { + // Map the type to the number of types added before it + let entries = self.types.len(); + self.types.entry(&ty).or_insert(entries); + } } else { - debug!("no type in expr = {:?}, span = {:?}", expr, expr.map(|e| e.span)); + debug!("no type in expr = {:?}, count = {:?}, span = {:?}", + expr, self.expr_count, expr.map(|e| e.span)); } } } @@ -67,7 +85,7 @@ impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> { pub fn resolve_interior<'a, 'gcx, 'tcx>(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, def_id: DefId, body_id: hir::BodyId, - witness: Ty<'tcx>) { + interior: GeneratorInterior<'tcx>) { let body = fcx.tcx.hir.body(body_id); let mut visitor = InteriorVisitor { fcx, @@ -87,17 +105,40 @@ pub fn resolve_interior<'a, 'gcx, 'tcx>(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, types.sort_by_key(|t| t.1); // Extract type components - let types: Vec<_> = types.into_iter().map(|t| t.0).collect(); - - let tuple = fcx.tcx.intern_tup(&types, false); - - debug!("Types in generator {:?}, span = {:?}", tuple, body.value.span); - - // Unify the tuple with the witness - match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(witness, tuple) { + let type_list = fcx.tcx.mk_type_list(types.into_iter().map(|t| t.0)); + + // The types in the generator interior contain lifetimes local to the generator itself, + // which should not be exposed outside of the generator. Therefore, we replace these + // lifetimes with existentially-bound lifetimes, which reflect the exact value of the + // lifetimes not being known by users. + // + // These lifetimes are used in auto trait impl checking (for example, + // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), + // so knowledge of the exact relationships between them isn't particularly important. + + debug!("Types in generator {:?}, span = {:?}", type_list, body.value.span); + + // Replace all regions inside the generator interior with late bound regions + // Note that each region slot in the types gets a new fresh late bound region, + // which means that none of the regions inside relate to any other, even if + // typeck had previously found contraints that would cause them to be related. + let mut counter = 0; + let type_list = fcx.tcx.fold_regions(&type_list, &mut false, |_, current_depth| { + counter += 1; + fcx.tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(current_depth), + ty::BrAnon(counter))) + }); + + let witness = fcx.tcx.mk_generator_witness(ty::Binder(type_list)); + + debug!("Types in generator after region replacement {:?}, span = {:?}", + witness, body.value.span); + + // Unify the type variable inside the generator with the new witness + match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior.witness, witness) { Ok(ok) => fcx.register_infer_ok_obligations(ok), _ => bug!(), - } + } } // This visitor has to have the same visit_expr calls as RegionResolutionVisitor in @@ -112,7 +153,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'gcx, 'tcx> { 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); + self.record(ty, Some(scope), None, pat.span); } self.expr_count += 1; @@ -128,6 +169,6 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'gcx, 'tcx> { let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); let ty = self.fcx.tables.borrow().expr_ty_adjusted(expr); - self.record(ty, scope, Some(expr)); + self.record(ty, scope, Some(expr), expr.span); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index cb8ce8d5ac331..5b7eca7a30113 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -209,7 +209,7 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { deferred_cast_checks: RefCell>>, - deferred_generator_interiors: RefCell)>>, + deferred_generator_interiors: RefCell)>>, // Anonymized types found in explicit return types and their // associated fresh inference variable. Writeback resolves these @@ -838,7 +838,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env, &fn_sig); - let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, false).0; + let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0; fcx } else { let fcx = FnCtxt::new(&inh, param_env, body.value.id); @@ -973,7 +973,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, decl: &'gcx hir::FnDecl, fn_id: ast::NodeId, body: &'gcx hir::Body, - can_be_generator: bool) + can_be_generator: Option) -> (FnCtxt<'a, 'gcx, 'tcx>, Option>) { let mut fn_sig = fn_sig.clone(); @@ -999,7 +999,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, let span = body.value.span; - if body.is_generator && can_be_generator { + if body.is_generator && can_be_generator.is_some() { fcx.yield_ty = Some(fcx.next_ty_var(TypeVariableOrigin::TypeInference(span))); } @@ -1023,17 +1023,24 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, } let fn_hir_id = fcx.tcx.hir.node_to_hir_id(fn_id); - let gen_ty = if can_be_generator && body.is_generator { + inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_hir_id, fn_sig); + + fcx.check_return_expr(&body.value); + + // We insert the deferred_generator_interiors entry after visiting the body. + // This ensures that all nested generators appear before the entry of this generator. + // resolve_generator_interiors relies on this property. + let gen_ty = if can_be_generator.is_some() && body.is_generator { let witness = fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span)); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), witness)); - let interior = ty::GeneratorInterior::new(witness); + let interior = ty::GeneratorInterior { + witness, + movable: can_be_generator.unwrap() == hir::GeneratorMovability::Movable, + }; + fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior)); Some(GeneratorTypes { yield_ty: fcx.yield_ty.unwrap(), interior: interior }) } else { None }; - inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_hir_id, fn_sig); - - fcx.check_return_expr(&body.value); // Finalize the return check by taking the LUB of the return types // we saw and assigning it to the expected return type. This isn't @@ -2113,9 +2120,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } fn resolve_generator_interiors(&self, def_id: DefId) { - let mut deferred_generator_interiors = self.deferred_generator_interiors.borrow_mut(); - for (body_id, witness) in deferred_generator_interiors.drain(..) { - generator_interior::resolve_interior(self, def_id, body_id, witness); + let mut generators = self.deferred_generator_interiors.borrow_mut(); + for (body_id, interior) in generators.drain(..) { + self.select_obligations_where_possible(); + generator_interior::resolve_interior(self, def_id, body_id, interior); } } @@ -3854,8 +3862,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::ExprMatch(ref discrim, ref arms, match_src) => { self.check_match(expr, &discrim, arms, expected, match_src) } - hir::ExprClosure(capture, ref decl, body_id, _, _) => { - self.check_expr_closure(expr, capture, &decl, body_id, expected) + hir::ExprClosure(capture, ref decl, body_id, _, gen) => { + self.check_expr_closure(expr, capture, &decl, body_id, gen, expected) } hir::ExprBlock(ref body) => { self.check_block_with_expected(&body, expected) diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 2e0d0ddfc3936..ab148afafbe09 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -74,11 +74,11 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> { fn visit_expr(&mut self, expr: &'gcx hir::Expr) { match expr.node { - hir::ExprClosure(cc, _, body_id, _, is_generator) => { + hir::ExprClosure(cc, _, body_id, _, gen) => { let body = self.fcx.tcx.hir.body(body_id); self.visit_body(body); self.fcx - .analyze_closure(expr.id, expr.hir_id, expr.span, body, cc, is_generator); + .analyze_closure(expr.id, expr.hir_id, expr.span, body, cc, gen); } _ => {} @@ -96,7 +96,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { span: Span, body: &hir::Body, capture_clause: hir::CaptureClause, - is_generator: bool, + gen: Option, ) { /*! * Analysis starting point. @@ -121,7 +121,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - let infer_kind = if is_generator { + let infer_kind = if gen.is_some() { false } else { self.closure_kind(closure_def_id, closure_substs).is_none() diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5485045b70438..8d428c860c50a 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1141,8 +1141,8 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, NodeField(field) => icx.to_ty(&field.ty), - NodeExpr(&hir::Expr { node: hir::ExprClosure(.., is_generator), .. }) => { - if is_generator { + NodeExpr(&hir::Expr { node: hir::ExprClosure(.., gen), .. }) => { + if gen.is_some() { let hir_id = tcx.hir.node_to_hir_id(node_id); return tcx.typeck_tables_of(def_id).node_id_to_type(hir_id); } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 4f661a4f5e67d..18d573bd581eb 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4776,4 +4776,5 @@ register_diagnostics! { // argument position. E0641, // cannot cast to/from a pointer with an unknown kind E0645, // trait aliases not finished + E0907, // type inside generator must be known in this context } diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index df42d5eaa0a3d..0ed9b14b9d1d4 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -377,6 +377,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // types, where we use TyError as the Self type } + ty::TyGeneratorWitness(..) | ty::TyInfer(..) => { bug!("unexpected type encountered in \ variance inference: {}", diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index cc75664cacbcc..22acf365e8557 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2314,6 +2314,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::TyClosure(..) | ty::TyGenerator(..) => Tuple(vec![]), // FIXME(pcwalton) + ty::TyGeneratorWitness(..) => panic!("TyGeneratorWitness"), ty::TyInfer(..) => panic!("TyInfer"), ty::TyError => panic!("TyError"), } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a64f1e9e4002c..d6e26057ea81c 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1100,7 +1100,7 @@ pub enum ExprKind { /// A closure (for example, `move |a, b, c| a + b + c`) /// /// The final span is the span of the argument block `|...|` - Closure(CaptureBy, P, P, Span), + Closure(CaptureBy, Movability, P, P, Span), /// A block (`{ ... }`) Block(P), /// A catch block (`catch { ... }`) @@ -1194,6 +1194,13 @@ pub enum CaptureBy { Ref, } +/// The movability of a generator / closure literal +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +pub enum Movability { + Static, + Movable, +} + pub type Mac = Spanned; /// Represents a macro invocation. The Path indicates which macro diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 7f7ff56fd7fbe..cf63592c2ece2 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -912,6 +912,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn_decl_span: Span) // span of the `|...|` part -> P { self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, + ast::Movability::Movable, fn_decl, body, fn_decl_span)) @@ -930,7 +931,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> { // part of the lambda, but it probably (maybe?) corresponds to // the entire lambda body. Probably we should extend the API // here, but that's not entirely clear. - self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, fn_decl, body, span)) + self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, + ast::Movability::Movable, + fn_decl, + body, + span)) } fn lambda0(&self, span: Span, body: P) -> P { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index c304e3a9f5052..1e605ba3ecdfb 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1235,8 +1235,9 @@ pub fn noop_fold_expr(Expr {id, node, span, attrs}: Expr, folder: &mu ExprKind::Match(folder.fold_expr(expr), arms.move_map(|x| folder.fold_arm(x))) } - ExprKind::Closure(capture_clause, decl, body, span) => { + ExprKind::Closure(capture_clause, movability, decl, body, span) => { ExprKind::Closure(capture_clause, + movability, folder.fold_fn_decl(decl), folder.fold_expr(body), folder.new_span(span)) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3d58104260f9a..8213d604b91b2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -14,7 +14,7 @@ use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use ast::Unsafety; use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind}; use ast::Block; -use ast::{BlockCheckMode, CaptureBy}; +use ast::{BlockCheckMode, CaptureBy, Movability}; use ast::{Constness, Crate}; use ast::Defaultness; use ast::EnumDef; @@ -2258,8 +2258,7 @@ impl<'a> Parser<'a> { return self.parse_block_expr(lo, BlockCheckMode::Default, attrs); } token::BinOp(token::Or) | token::OrOr => { - let lo = self.span; - return self.parse_lambda_expr(lo, CaptureBy::Ref, attrs); + return self.parse_lambda_expr(attrs); } token::OpenDelim(token::Bracket) => { self.bump(); @@ -2304,9 +2303,8 @@ impl<'a> Parser<'a> { hi = path.span; return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs)); } - if self.eat_keyword(keywords::Move) { - let lo = self.prev_span; - return self.parse_lambda_expr(lo, CaptureBy::Value, attrs); + if self.check_keyword(keywords::Move) || self.check_keyword(keywords::Static) { + return self.parse_lambda_expr(attrs); } if self.eat_keyword(keywords::If) { return self.parse_if_expr(attrs); @@ -3247,11 +3245,20 @@ impl<'a> Parser<'a> { // `move |args| expr` pub fn parse_lambda_expr(&mut self, - lo: Span, - capture_clause: CaptureBy, attrs: ThinVec) -> PResult<'a, P> { + let lo = self.span; + let movability = if self.eat_keyword(keywords::Static) { + Movability::Static + } else { + Movability::Movable + }; + let capture_clause = if self.eat_keyword(keywords::Move) { + CaptureBy::Value + } else { + CaptureBy::Ref + }; let decl = self.parse_fn_block_decl()?; let decl_hi = self.prev_span; let body = match decl.output { @@ -3269,7 +3276,7 @@ impl<'a> Parser<'a> { Ok(self.mk_expr( lo.to(body.span), - ExprKind::Closure(capture_clause, decl, body, lo.to(decl_hi)), + ExprKind::Closure(capture_clause, movability, decl, body, lo.to(decl_hi)), attrs)) } @@ -6271,6 +6278,23 @@ impl<'a> Parser<'a> { } } + fn is_static_global(&mut self) -> bool { + if self.check_keyword(keywords::Static) { + // Check if this could be a closure + !self.look_ahead(1, |token| { + if token.is_keyword(keywords::Move) { + return true; + } + match *token { + token::BinOp(token::Or) | token::OrOr => true, + _ => false, + } + }) + } else { + false + } + } + /// Parse one of the items allowed by the flags. /// NB: this function no longer parses the items inside an /// extern crate. @@ -6329,7 +6353,8 @@ impl<'a> Parser<'a> { self.unexpected()?; } - if self.eat_keyword(keywords::Static) { + if self.is_static_global() { + self.bump(); // STATIC ITEM let m = if self.eat_keyword(keywords::Mut) { Mutability::Mutable diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ff065b57b8d0b..345c592a01100 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2162,7 +2162,8 @@ impl<'a> State<'a> { } self.bclose_(expr.span, INDENT_UNIT)?; } - ast::ExprKind::Closure(capture_clause, ref decl, ref body, _) => { + ast::ExprKind::Closure(capture_clause, movability, ref decl, ref body, _) => { + self.print_movability(movability)?; self.print_capture_clause(capture_clause)?; self.print_fn_block_args(decl)?; @@ -2777,6 +2778,14 @@ impl<'a> State<'a> { } } + pub fn print_movability(&mut self, movability: ast::Movability) + -> io::Result<()> { + match movability { + ast::Movability::Static => self.word_space("static"), + ast::Movability::Movable => Ok(()), + } + } + pub fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) -> io::Result<()> { match capture_clause { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index b5fc9236ad39a..8aeacf79cee7d 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -739,7 +739,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure(_, ref function_declaration, ref body, _decl_span) => { + ExprKind::Closure(_, _, ref function_declaration, ref body, _decl_span) => { visitor.visit_fn(FnKind::Closure(body), function_declaration, expression.span, diff --git a/src/test/compile-fail/static-closures.rs b/src/test/compile-fail/static-closures.rs new file mode 100644 index 0000000000000..8593eb5333e39 --- /dev/null +++ b/src/test/compile-fail/static-closures.rs @@ -0,0 +1,14 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + static || {}; + //~^ ERROR closures cannot be static +} diff --git a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs index 5afa9a217e05c..10c44c8044b41 100644 --- a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs @@ -142,7 +142,11 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { variadic: false, }); iter_exprs(depth - 1, &mut |e| g( - ExprKind::Closure(CaptureBy::Value, decl.clone(), e, DUMMY_SP))); + ExprKind::Closure(CaptureBy::Value, + Movability::Movable, + decl.clone(), + e, + DUMMY_SP))); }, 10 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x()))); diff --git a/src/test/run-pass/generator/issue-44197.rs b/src/test/run-pass/generator/issue-44197.rs new file mode 100644 index 0000000000000..7cb80ea8b21b7 --- /dev/null +++ b/src/test/run-pass/generator/issue-44197.rs @@ -0,0 +1,40 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait, generators, generator_trait)] + +use std::ops::{ Generator, GeneratorState }; + +fn foo(_: &str) -> String { + String::new() +} + +fn bar(baz: String) -> impl Generator { + move || { + yield foo(&baz); + } +} + +fn foo2(_: &str) -> Result { + Err(()) +} + +fn bar2(baz: String) -> impl Generator { + move || { + if let Ok(quux) = foo2(&baz) { + yield quux; + } + } +} + +fn main() { + assert_eq!(bar(String::new()).resume(), GeneratorState::Yielded(String::new())); + assert_eq!(bar2(String::new()).resume(), GeneratorState::Complete(())); +} diff --git a/src/test/run-pass/generator/live-upvar-across-yield.rs b/src/test/run-pass/generator/live-upvar-across-yield.rs new file mode 100644 index 0000000000000..e34b0b3100c32 --- /dev/null +++ b/src/test/run-pass/generator/live-upvar-across-yield.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , 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 b = |_| 3; + let mut a = || { + b(yield); + }; + a.resume(); +} diff --git a/src/test/run-pass/generator/nested_generators.rs b/src/test/run-pass/generator/nested_generators.rs new file mode 100644 index 0000000000000..f70d4144a3c9e --- /dev/null +++ b/src/test/run-pass/generator/nested_generators.rs @@ -0,0 +1,30 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators)] +#![feature(generator_trait)] + +use std::ops::Generator; +use std::ops::GeneratorState; + +fn main() { + let _generator = || { + let mut sub_generator = || { + yield 2; + }; + + match sub_generator.resume() { + GeneratorState::Yielded(x) => { + yield x; + } + _ => panic!(), + }; + }; +} diff --git a/src/test/run-pass/generator/reborrow-mut-upvar.rs b/src/test/run-pass/generator/reborrow-mut-upvar.rs new file mode 100644 index 0000000000000..8353066bfbe8e --- /dev/null +++ b/src/test/run-pass/generator/reborrow-mut-upvar.rs @@ -0,0 +1,24 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators)] + +fn _run(bar: &mut i32) { + || { + { + let _baz = &*bar; + yield; + } + + *bar = 2; + }; +} + +fn main() {} diff --git a/src/test/run-pass/generator/static-generators.rs b/src/test/run-pass/generator/static-generators.rs new file mode 100644 index 0000000000000..daf672942467c --- /dev/null +++ b/src/test/run-pass/generator/static-generators.rs @@ -0,0 +1,24 @@ +// 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 or the MIT license +// , 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, GeneratorState}; + +fn main() { + let mut generator = static || { + let a = true; + let b = &a; + yield; + assert_eq!(b as *const _, &a as *const _); + }; + assert_eq!(generator.resume(), GeneratorState::Yielded(())); + assert_eq!(generator.resume(), GeneratorState::Complete(())); +} \ No newline at end of file diff --git a/src/test/ui/generator/auto-trait-regions.rs b/src/test/ui/generator/auto-trait-regions.rs new file mode 100644 index 0000000000000..ac2a414b742c6 --- /dev/null +++ b/src/test/ui/generator/auto-trait-regions.rs @@ -0,0 +1,58 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators)] +#![feature(optin_builtin_traits)] + +auto trait Foo {} + +struct No; + +impl !Foo for No {} + +struct A<'a, 'b>(&'a mut bool, &'b mut bool, No); + +impl<'a, 'b: 'a> Foo for A<'a, 'b> {} + +struct OnlyFooIfStaticRef(No); +impl Foo for &'static OnlyFooIfStaticRef {} + +struct OnlyFooIfRef(No); +impl<'a> Foo for &'a OnlyFooIfRef {} + +fn assert_foo(f: T) {} + +fn main() { + // Make sure 'static is erased for generator interiors so we can't match it in trait selection + let x: &'static _ = &OnlyFooIfStaticRef(No); + let gen = || { + let x = x; + yield; + assert_foo(x); + }; + assert_foo(gen); //~ ERROR the trait bound `No: Foo` is not satisfied + + // Allow impls which matches any lifetime + let x = &OnlyFooIfRef(No); + let gen = || { + let x = x; + yield; + assert_foo(x); + }; + assert_foo(gen); // ok + + // Disallow impls which relates lifetimes in the generator interior + let gen = || { + let a = A(&mut true, &mut true, No); + yield; + assert_foo(a); + }; + assert_foo(gen); //~ ERROR the requirement `for<'r, 's> 'r : 's` is not satisfied +} diff --git a/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr new file mode 100644 index 0000000000000..37241e615101d --- /dev/null +++ b/src/test/ui/generator/auto-trait-regions.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `No: Foo` is not satisfied in `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&'static OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]` + --> $DIR/auto-trait-regions.rs:40:5 + | +40 | assert_foo(gen); //~ ERROR the trait bound `No: Foo` is not satisfied + | ^^^^^^^^^^ within `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&'static OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]`, the trait `Foo` is not implemented for `No` + | + = help: the following implementations were found: + + = note: required because it appears within the type `OnlyFooIfStaticRef` + = note: required because it appears within the type `&OnlyFooIfStaticRef` + = note: required because it appears within the type `for<'r> {&'r OnlyFooIfStaticRef, ()}` + = note: required because it appears within the type `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&'static OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]` +note: required by `assert_foo` + --> $DIR/auto-trait-regions.rs:30:1 + | +30 | fn assert_foo(f: T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0279]: the requirement `for<'r, 's> 'r : 's` is not satisfied (`expected bound lifetime parameter, found concrete lifetime`) + --> $DIR/auto-trait-regions.rs:57:5 + | +57 | assert_foo(gen); //~ ERROR the requirement `for<'r, 's> 'r : 's` is not satisfied + | ^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `for<'r, 's> Foo` for `A<'_, '_>` + = note: required because it appears within the type `for<'r, 's> {A<'r, 's>, ()}` + = note: required because it appears within the type `[generator@$DIR/auto-trait-regions.rs:52:15: 56:6 for<'r, 's> {A<'r, 's>, ()}]` +note: required by `assert_foo` + --> $DIR/auto-trait-regions.rs:30:1 + | +30 | fn assert_foo(f: T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/generator/not-send-sync.stderr b/src/test/ui/generator/not-send-sync.stderr index d30255335a660..e65c8f1546e82 100644 --- a/src/test/ui/generator/not-send-sync.stderr +++ b/src/test/ui/generator/not-send-sync.stderr @@ -13,15 +13,15 @@ note: required by `main::assert_send` 17 | fn assert_send(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `std::cell::Cell: std::marker::Sync` is not satisfied in `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell, ())]` +error[E0277]: the trait bound `std::cell::Cell: std::marker::Sync` is not satisfied in `[generator@$DIR/not-send-sync.rs:19:17: 23:6 {std::cell::Cell, ()}]` --> $DIR/not-send-sync.rs:19:5 | 19 | assert_sync(|| { | ^^^^^^^^^^^ `std::cell::Cell` cannot be shared between threads safely | - = help: within `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell, ())]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell` - = note: required because it appears within the type `(std::cell::Cell, ())` - = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell, ())]` + = help: within `[generator@$DIR/not-send-sync.rs:19:17: 23:6 {std::cell::Cell, ()}]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell` + = note: required because it appears within the type `{std::cell::Cell, ()}` + = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:19:17: 23:6 {std::cell::Cell, ()}]` note: required by `main::assert_sync` --> $DIR/not-send-sync.rs:16:5 | From c166ef9d1b5215186ef8863390d87783ba46e432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 22 Dec 2017 22:12:27 +0100 Subject: [PATCH 2/6] Make immovable generators unsafe --- src/librustc_mir/transform/check_unsafety.rs | 12 ++++++++++-- .../run-pass/generator/static-generators.rs | 14 ++++++++------ src/test/ui/generator/unsafe-immovable.rs | 17 +++++++++++++++++ src/test/ui/generator/unsafe-immovable.stderr | 10 ++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/generator/unsafe-immovable.rs create mode 100644 src/test/ui/generator/unsafe-immovable.stderr diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index e9ba5de3cc694..ae27f54e618a1 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -124,13 +124,21 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { &AggregateKind::Array(..) | &AggregateKind::Tuple | &AggregateKind::Adt(..) => {} - &AggregateKind::Closure(def_id, _) | - &AggregateKind::Generator(def_id, _, _) => { + &AggregateKind::Closure(def_id, _) => { let UnsafetyCheckResult { violations, unsafe_blocks } = self.tcx.unsafety_check_result(def_id); self.register_violations(&violations, &unsafe_blocks); } + &AggregateKind::Generator(def_id, _, interior) => { + let UnsafetyCheckResult { + violations, unsafe_blocks + } = self.tcx.unsafety_check_result(def_id); + self.register_violations(&violations, &unsafe_blocks); + if !interior.movable { + self.require_unsafe("construction of immovable generator") + } + } } } self.super_rvalue(rvalue, location); diff --git a/src/test/run-pass/generator/static-generators.rs b/src/test/run-pass/generator/static-generators.rs index daf672942467c..9504414d8b6f5 100644 --- a/src/test/run-pass/generator/static-generators.rs +++ b/src/test/run-pass/generator/static-generators.rs @@ -13,12 +13,14 @@ use std::ops::{Generator, GeneratorState}; fn main() { - let mut generator = static || { - let a = true; - let b = &a; - yield; - assert_eq!(b as *const _, &a as *const _); + let mut generator = unsafe { + static || { + let a = true; + let b = &a; + yield; + assert_eq!(b as *const _, &a as *const _); + } }; assert_eq!(generator.resume(), GeneratorState::Yielded(())); assert_eq!(generator.resume(), GeneratorState::Complete(())); -} \ No newline at end of file +} diff --git a/src/test/ui/generator/unsafe-immovable.rs b/src/test/ui/generator/unsafe-immovable.rs new file mode 100644 index 0000000000000..45acbf50931bc --- /dev/null +++ b/src/test/ui/generator/unsafe-immovable.rs @@ -0,0 +1,17 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators)] + +fn main() { + static || { //~ ERROR: construction of immovable generator requires unsafe + yield; + }; +} diff --git a/src/test/ui/generator/unsafe-immovable.stderr b/src/test/ui/generator/unsafe-immovable.stderr new file mode 100644 index 0000000000000..06e43bf35e1fa --- /dev/null +++ b/src/test/ui/generator/unsafe-immovable.stderr @@ -0,0 +1,10 @@ +error[E0133]: construction of immovable generator requires unsafe function or block + --> $DIR/unsafe-immovable.rs:14:5 + | +14 | / static || { //~ ERROR: construction of immovable generator requires unsafe +15 | | yield; +16 | | }; + | |_____^ construction of immovable generator + +error: aborting due to previous error + From 9b57e60f030e9a6b8b633e7ddfaaed611a8a9a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 11 Jan 2018 19:46:51 +0100 Subject: [PATCH 3/6] Use delay_span_bug for things that should be errors --- src/librustc_borrowck/borrowck/check_loans.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 7252a1795395b..862ea0c240ab9 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -416,7 +416,14 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { RegionKind::ReEarlyBound(..) | RegionKind::ReLateBound(..) | RegionKind::ReFree(..) | - RegionKind::ReStatic => return, + RegionKind::ReStatic => { + self.bccx + .tcx + .sess.delay_span_bug(borrow_span, + &format!("unexpected region for local data {:?}", + loan_region)); + return + } // These cannot exist in borrowck RegionKind::ReVar(..) | @@ -430,9 +437,10 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { let body_id = self.bccx.body.value.hir_id.local_id; if self.bccx.region_scope_tree.containing_body(scope) != Some(body_id) { - // We are borrowing a local data longer than it's storage. + // We are borrowing local data longer than its storage. // This should result in other borrowck errors. - // FIXME: Ensure an error is generated + self.bccx.tcx.sess.delay_span_bug(borrow_span, + "borrowing local data longer than its storage"); return; } From bae2988e6f12560ebe86bbff9daa75a7e81d59dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 11 Jan 2018 19:49:26 +0100 Subject: [PATCH 4/6] Fix yield-while-local-borrowed.rs test --- .../generator/yield-while-local-borrowed.rs | 12 ++++-- .../yield-while-local-borrowed.stderr | 38 ++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/test/ui/generator/yield-while-local-borrowed.rs b/src/test/ui/generator/yield-while-local-borrowed.rs index 504f3e8739f24..11bd4ed05caca 100644 --- a/src/test/ui/generator/yield-while-local-borrowed.rs +++ b/src/test/ui/generator/yield-while-local-borrowed.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z borrowck=compare + #![feature(generators, generator_trait)] use std::ops::{GeneratorState, Generator}; @@ -19,7 +21,9 @@ fn borrow_local_inline() { // (This error occurs because the region shows up in the type of // `b` and gets extended by region inference.) let mut b = move || { - let a = &3; + let a = &mut 3; + //~^ ERROR borrow may still be in use when generator yields (Ast) + //~| ERROR borrow may still be in use when generator yields (Mir) yield(); println!("{}", a); }; @@ -30,7 +34,7 @@ fn borrow_local_inline_done() { // No error here -- `a` is not in scope at the point of `yield`. let mut b = move || { { - let a = &3; + let a = &mut 3; } yield(); }; @@ -45,7 +49,9 @@ fn borrow_local() { let mut b = move || { let a = 3; { - let b = &a; //~ ERROR + let b = &a; + //~^ ERROR borrow may still be in use when generator yields (Ast) + //~| ERROR borrow may still be in use when generator yields (Mir) yield(); println!("{}", b); } diff --git a/src/test/ui/generator/yield-while-local-borrowed.stderr b/src/test/ui/generator/yield-while-local-borrowed.stderr index 2fe6c686ce366..7961dd9744136 100644 --- a/src/test/ui/generator/yield-while-local-borrowed.stderr +++ b/src/test/ui/generator/yield-while-local-borrowed.stderr @@ -1,10 +1,38 @@ -error[E0626]: borrow may still be in use when generator yields - --> $DIR/yield-while-local-borrowed.rs:48:22 +error[E0626]: borrow may still be in use when generator yields (Mir) + --> $DIR/yield-while-local-borrowed.rs:24:17 | -48 | let b = &a; //~ ERROR +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 + | +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:52:22 + | +52 | let b = &a; | ^ -49 | yield(); +... +55 | 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 + | +52 | let b = &a; + | ^^ +... +55 | yield(); | ------- possible yield occurs here -error: aborting due to previous error +error: aborting due to 4 previous errors From 410d27bb9730c9843ae79aeb8e42533c43e429d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 11 Jan 2018 19:50:01 +0100 Subject: [PATCH 5/6] Add dropck test --- src/test/ui/generator/dropck.rs | 28 ++++++++++++++++++++++++++++ src/test/ui/generator/dropck.stderr | 16 ++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/test/ui/generator/dropck.rs create mode 100644 src/test/ui/generator/dropck.stderr diff --git a/src/test/ui/generator/dropck.rs b/src/test/ui/generator/dropck.rs new file mode 100644 index 0000000000000..0b143d7f5143e --- /dev/null +++ b/src/test/ui/generator/dropck.rs @@ -0,0 +1,28 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait, box_leak)] + +use std::cell::RefCell; +use std::ops::Generator; + +fn main() { + let (cell, mut gen); + cell = Box::new(RefCell::new(0)); + let ref_ = Box::leak(Box::new(Some(cell.borrow_mut()))); + // the upvar is the non-dropck `&mut Option>`. + gen = || { + // but the generator can use it to drop a `Ref<'a, i32>`. + let _d = ref_.take(); //~ ERROR `ref_` does not live long enough + yield; + }; + gen.resume(); + // drops the RefCell and then the Ref, leading to use-after-free +} diff --git a/src/test/ui/generator/dropck.stderr b/src/test/ui/generator/dropck.stderr new file mode 100644 index 0000000000000..deaf00fff071e --- /dev/null +++ b/src/test/ui/generator/dropck.stderr @@ -0,0 +1,16 @@ +error[E0597]: `ref_` does not live long enough + --> $DIR/dropck.rs:23:18 + | +21 | gen = || { + | -- capture occurs here +22 | // but the generator can use it to drop a `Ref<'a, i32>`. +23 | let _d = ref_.take(); //~ ERROR `ref_` does not live long enough + | ^^^^ borrowed value does not live long enough +... +28 | } + | - borrowed value dropped before borrower + | + = note: values in a scope are dropped in the opposite order they are created + +error: aborting due to previous error + From 55c6c88782cfcce9b593c431200dad5f05bd9125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 11 Jan 2018 19:50:40 +0100 Subject: [PATCH 6/6] Port borrows across yield check to MIR borrowck --- .../borrow_check/error_reporting.rs | 2 +- src/librustc_mir/borrow_check/mod.rs | 61 +++++++++++++++++++ src/test/ui/generator/generator-with-nll.rs | 25 ++++++++ .../ui/generator/generator-with-nll.stderr | 29 +++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/generator/generator-with-nll.rs create mode 100644 src/test/ui/generator/generator-with-nll.stderr diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 520febca938f0..7bd3b6e39f053 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -769,7 +769,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } // Retrieve span of given borrow from the current MIR representation - fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { + pub fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { self.mir.source_info(borrow.location).span } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index b5836c65d675b..d6937c405f961 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -209,12 +209,21 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( }; let flow_inits = flow_inits; // remove mut + let movable_generator = !match tcx.hir.get(id) { + hir::map::Node::NodeExpr(&hir::Expr { + node: hir::ExprClosure(.., Some(hir::GeneratorMovability::Static)), + .. + }) => true, + _ => false, + }; + let mut mbcx = MirBorrowckCtxt { tcx: tcx, mir: mir, node_id: id, move_data: &mdpe.move_data, param_env: param_env, + movable_generator, locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false, hir::BodyOwnerKind::Fn => true, @@ -277,6 +286,7 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { node_id: ast::NodeId, move_data: &'cx MoveData<'tcx>, param_env: ParamEnv<'gcx>, + movable_generator: bool, /// This keeps track of whether local variables are free-ed when the function /// exits even without a `StorageDead`, which appears to be the case for /// constants. @@ -534,6 +544,18 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx drop: _, } => { self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state); + + if self.movable_generator { + // Look for any active borrows to locals + let domain = flow_state.borrows.operator(); + let data = domain.borrows(); + flow_state.borrows.with_elems_outgoing(|borrows| { + for i in borrows { + let borrow = &data[i.borrow_index()]; + self.check_for_local_borrow(borrow, span); + } + }); + } } TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { @@ -1099,6 +1121,45 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } + /// Reports an error if this is a borrow of local data. + /// This is called for all Yield statements on movable generators + fn check_for_local_borrow( + &mut self, + borrow: &BorrowData<'tcx>, + yield_span: Span) + { + fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool { + match place { + Place::Static(..) => false, + Place::Local(..) => true, + Place::Projection(box proj) => { + match proj.elem { + // Reborrow of already borrowed data is ignored + // Any errors will be caught on the initial borrow + ProjectionElem::Deref => false, + + // For interior references and downcasts, find out if the base is local + ProjectionElem::Field(..) | + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | + ProjectionElem::Downcast(..) => { + borrow_of_local_data(&proj.base) + } + } + } + } + } + + debug!("check_for_local_borrow({:?})", borrow); + + if borrow_of_local_data(&borrow.borrowed_place) { + self.tcx.cannot_borrow_across_generator_yield(self.retrieve_borrow_span(borrow), + yield_span, + Origin::Mir).emit(); + } + } + fn check_activations( &mut self, location: Location, diff --git a/src/test/ui/generator/generator-with-nll.rs b/src/test/ui/generator/generator-with-nll.rs new file mode 100644 index 0000000000000..3223ff4dc8b59 --- /dev/null +++ b/src/test/ui/generator/generator-with-nll.rs @@ -0,0 +1,25 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z borrowck=compare + +#![feature(generators)] +#![feature(nll)] + +fn main() { + || { + // The reference in `_a` is a Legal with NLL since it ends before the yield + let _a = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast) + let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast) + //~^ borrow may still be in use when generator yields (Mir) + yield (); + println!("{}", b); + }; +} diff --git a/src/test/ui/generator/generator-with-nll.stderr b/src/test/ui/generator/generator-with-nll.stderr new file mode 100644 index 0000000000000..0a52a928f69d4 --- /dev/null +++ b/src/test/ui/generator/generator-with-nll.stderr @@ -0,0 +1,29 @@ +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 + | +19 | let _a = &mut true; //~ ERROR 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 (Ast) + --> $DIR/generator-with-nll.rs:20:22 + | +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 +