From f60a7c4798e0c7e7b2c6814eb55b35f4b4769fe2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jul 2014 05:27:46 -0400 Subject: [PATCH] Fix regionck to consider bounds on a proc when capturing variables --- src/librustc/middle/typeck/check/regionck.rs | 6 ++++ .../middle/typeck/infer/error_reporting.rs | 30 +++++++++++++--- src/librustc/middle/typeck/infer/mod.rs | 7 ++++ .../regions-infer-proc-static-upvar.rs | 34 +++++++++++++++++++ .../regions-infer-static-from-proc.rs | 23 +++++++++++++ 5 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 src/test/compile-fail/regions-infer-proc-static-upvar.rs create mode 100644 src/test/run-pass/regions-infer-static-from-proc.rs diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 72f33a2f984..e7676301946 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -1534,6 +1534,12 @@ fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx, fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx, cmt: mc::cmt) { + /*! + * Indicates that `cmt` is being directly mutated (e.g., assigned + * to). If cmt contains any by-ref upvars, this implies that + * those upvars must be borrowed using an `&mut` borow. + */ + let mut cmt = cmt; loop { debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 2883a960df9..bd355d2f580 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -583,6 +583,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sub, ""); } + infer::ProcCapture(span, id) => { + self.tcx.sess.span_err( + span, + format!("captured variable `{}` must be 'static \ + to be captured in a proc", + ty::local_var_name_str(self.tcx, id).get()) + .as_slice()); + note_and_explain_region( + self.tcx, + "captured variable is only valid for ", + sup, + ""); + } infer::IndexSlice(span) => { self.tcx.sess.span_err(span, "index of slice outside its lifetime"); @@ -1423,11 +1436,11 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { bound_region_to_string(self.tcx, "lifetime parameter ", true, br)) } infer::EarlyBoundRegion(_, name) => { - format!(" for lifetime parameter `{}", + format!(" for lifetime parameter `{}`", token::get_name(name).get()) } infer::BoundRegionInCoherence(name) => { - format!(" for lifetime parameter `{} in coherence check", + format!(" for lifetime parameter `{}` in coherence check", token::get_name(name).get()) } infer::UpvarRegion(ref upvar_id, _) => { @@ -1528,6 +1541,15 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { self.tcx, id).get().to_string()).as_slice()); } + infer::ProcCapture(span, id) => { + self.tcx.sess.span_note( + span, + format!("...so that captured variable `{}` \ + is 'static", + ty::local_var_name_str( + self.tcx, + id).get()).as_slice()); + } infer::IndexSlice(span) => { self.tcx.sess.span_note( span, @@ -1571,8 +1593,8 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { infer::AutoBorrow(span) => { self.tcx.sess.span_note( span, - "...so that reference is valid \ - at the time of implicit borrow"); + "...so that auto-reference is valid \ + at the time of borrow"); } infer::ExprTypeIsNotInScope(t, span) => { self.tcx.sess.span_note( diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index ed96effdd83..c6312ec4663 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -161,6 +161,9 @@ pub enum SubregionOrigin { // Closure bound must not outlive captured free variables FreeVariable(Span, ast::NodeId), + // Proc upvars must be 'static + ProcCapture(Span, ast::NodeId), + // Index into slice must be within its lifetime IndexSlice(Span), @@ -933,6 +936,7 @@ impl SubregionOrigin { InvokeClosure(a) => a, DerefPointer(a) => a, FreeVariable(a, _) => a, + ProcCapture(a, _) => a, IndexSlice(a) => a, RelateObjectBound(a) => a, RelateProcBound(a, _, _) => a, @@ -972,6 +976,9 @@ impl Repr for SubregionOrigin { FreeVariable(a, b) => { format!("FreeVariable({}, {})", a.repr(tcx), b) } + ProcCapture(a, b) => { + format!("ProcCapture({}, {})", a.repr(tcx), b) + } IndexSlice(a) => { format!("IndexSlice({})", a.repr(tcx)) } diff --git a/src/test/compile-fail/regions-infer-proc-static-upvar.rs b/src/test/compile-fail/regions-infer-proc-static-upvar.rs new file mode 100644 index 00000000000..7fe85290da0 --- /dev/null +++ b/src/test/compile-fail/regions-infer-proc-static-upvar.rs @@ -0,0 +1,34 @@ +// Copyright 2014 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. + +// Test that, when a variable of type `&T` is captured inside a proc, +// we correctly infer/require that its lifetime is 'static. + +fn foo(_p: proc():'static) { } + +static i: int = 3; + +fn capture_local() { + let x = 3i; + let y = &x; //~ ERROR `x` does not live long enough + foo(proc() { + let _a = *y; + }); +} + +fn capture_static() { + // Legal because &i can have static lifetime: + let y = &i; + foo(proc() { + let _a = *y; + }); +} + +fn main() { } diff --git a/src/test/run-pass/regions-infer-static-from-proc.rs b/src/test/run-pass/regions-infer-static-from-proc.rs new file mode 100644 index 00000000000..823644ddfb5 --- /dev/null +++ b/src/test/run-pass/regions-infer-static-from-proc.rs @@ -0,0 +1,23 @@ +// Copyright 2014 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. + +// Check that the 'static bound on a proc influences lifetimes of +// region variables contained within (otherwise, region inference will +// give `x` a very short lifetime). + +static i: uint = 3; +fn foo(_: proc():'static) {} +fn read(_: uint) { } +pub fn main() { + let x = &i; + foo(proc() { + read(*x); + }); +}