diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 67746dfa330c6..cae7bf5c25c81 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -258,10 +258,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn report_region_errors(&self, region_scope_tree: ®ion::ScopeTree, - errors: &Vec>) { + errors: &Vec>, + will_later_be_reported_by_nll: bool) { debug!("report_region_errors(): {} errors to start", errors.len()); - if self.tcx.sess.opts.debugging_opts.nll { + if will_later_be_reported_by_nll && self.tcx.sess.nll() { + // With `#![feature(nll)]`, we want to present a nice user + // experience, so don't even mention the errors from the + // AST checker. + if self.tcx.sess.features.borrow().nll { + return; + } + + // But with -Znll, it's nice to have some note for later. for error in errors { match *error { RegionResolutionError::ConcreteFailure(ref origin, ..) | diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 32da08bf476cf..07c5b319970f8 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1159,10 +1159,45 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_type_vars_if_possible` as well as `fully_resolve`. - pub fn resolve_regions_and_report_errors(&self, - region_context: DefId, - region_map: ®ion::ScopeTree, - outlives_env: &OutlivesEnvironment<'tcx>) { + pub fn resolve_regions_and_report_errors( + &self, + region_context: DefId, + region_map: ®ion::ScopeTree, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + self.resolve_regions_and_report_errors_inner( + region_context, + region_map, + outlives_env, + false, + ) + } + + /// Like `resolve_regions_and_report_errors`, but skips error + /// reporting if NLL is enabled. This is used for fn bodies where + /// the same error may later be reported by the NLL-based + /// inference. + pub fn resolve_regions_and_report_errors_unless_nll( + &self, + region_context: DefId, + region_map: ®ion::ScopeTree, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + self.resolve_regions_and_report_errors_inner( + region_context, + region_map, + outlives_env, + true, + ) + } + + fn resolve_regions_and_report_errors_inner( + &self, + region_context: DefId, + region_map: ®ion::ScopeTree, + outlives_env: &OutlivesEnvironment<'tcx>, + will_later_be_reported_by_nll: bool, + ) { assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), "region_obligations not empty: {:#?}", self.region_obligations.borrow()); @@ -1187,7 +1222,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(region_map, &errors); // see error_reporting module + self.report_region_errors(region_map, &errors, will_later_be_reported_by_nll); } } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index a9200a3c8059a..60a218500ca78 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -18,7 +18,7 @@ use lint; use middle::allocator::AllocatorKind; use middle::dependency_format; use session::search_paths::PathKind; -use session::config::DebugInfoLevel; +use session::config::{BorrowckMode, DebugInfoLevel}; use ty::tls; use util::nodemap::{FxHashMap, FxHashSet}; use util::common::{duration_to_secs_str, ErrorReported}; @@ -437,11 +437,62 @@ impl Session { pub fn print_llvm_passes(&self) -> bool { self.opts.debugging_opts.print_llvm_passes } + + /// If true, we should use NLL-style region checking instead of + /// lexical style. + pub fn nll(&self) -> bool { + self.features.borrow().nll || self.opts.debugging_opts.nll + } + + /// If true, we should use the MIR-based borrowck (we may *also* use + /// the AST-based borrowck). + pub fn use_mir(&self) -> bool { + self.borrowck_mode().use_mir() + } + + /// If true, we should gather causal information during NLL + /// checking. This will eventually be the normal thing, but right + /// now it is too unoptimized. + pub fn nll_dump_cause(&self) -> bool { + self.opts.debugging_opts.nll_dump_cause + } + + /// If true, we should enable two-phase borrows checks. This is + /// done with either `-Ztwo-phase-borrows` or with + /// `#![feature(nll)]`. + pub fn two_phase_borrows(&self) -> bool { + self.features.borrow().nll || self.opts.debugging_opts.two_phase_borrows + } + + /// What mode(s) of borrowck should we run? AST? MIR? both? + /// (Also considers the `#![feature(nll)]` setting.) + pub fn borrowck_mode(&self) -> BorrowckMode { + match self.opts.borrowck_mode { + mode @ BorrowckMode::Mir | + mode @ BorrowckMode::Compare => mode, + + mode @ BorrowckMode::Ast => { + if self.nll() { + BorrowckMode::Mir + } else { + mode + } + } + + } + } + + /// Should we emit EndRegion MIR statements? These are consumed by + /// MIR borrowck, but not when NLL is used. They are also consumed + /// by the validation stuff. pub fn emit_end_regions(&self) -> bool { + // FIXME(#46875) -- we should not emit end regions when NLL is enabled, + // but for now we can't stop doing so because it causes false positives self.opts.debugging_opts.emit_end_regions || - (self.opts.debugging_opts.mir_emit_validate > 0) || - self.opts.borrowck_mode.use_mir() + self.opts.debugging_opts.mir_emit_validate > 0 || + self.use_mir() } + pub fn lto(&self) -> bool { self.opts.cg.lto || self.target.target.options.requires_lto } diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 62736db9260ac..b124872ba12ca 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -275,7 +275,7 @@ impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { o: Origin) -> DiagnosticBuilder<'a> { - if !o.should_emit_errors(self.tcx.sess.opts.borrowck_mode) { + if !o.should_emit_errors(self.tcx.sess.borrowck_mode()) { self.tcx.sess.diagnostic().cancel(&mut diag); } diag diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 46e97d95f493f..c907c97d6d3ed 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -72,10 +72,7 @@ fn mir_borrowck<'a, 'tcx>( let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); - if { - !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir() - && !tcx.sess.opts.debugging_opts.nll - } { + if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.use_mir() { return None; } @@ -104,7 +101,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( // contain non-lexical lifetimes. It will have a lifetime tied // to the inference context. let mut mir: Mir<'tcx> = input_mir.clone(); - let free_regions = if !tcx.sess.opts.debugging_opts.nll { + let free_regions = if !tcx.sess.nll() { None } else { let mir = &mut mir; @@ -207,7 +204,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( ); (Some(Rc::new(regioncx)), opt_closure_req) } else { - assert!(!tcx.sess.opts.debugging_opts.nll); + assert!(!tcx.sess.nll()); (None, None) }; let flow_inits = flow_inits; // remove mut @@ -774,7 +771,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => { // Reading from mere reservations of mutable-borrows is OK. - if this.tcx.sess.opts.debugging_opts.two_phase_borrows && index.is_reservation() + if this.tcx.sess.two_phase_borrows() && index.is_reservation() { return Control::Continue; } @@ -922,7 +919,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Unique | BorrowKind::Mut => { let wk = WriteKind::MutableBorrow(bk); - if self.tcx.sess.opts.debugging_opts.two_phase_borrows { + if self.tcx.sess.two_phase_borrows() { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) @@ -1112,7 +1109,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { span: Span, flow_state: &Flows<'cx, 'gcx, 'tcx>, ) { - if !self.tcx.sess.opts.debugging_opts.two_phase_borrows { + if !self.tcx.sess.two_phase_borrows() { return; } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 30ce17b6c6a6a..da136a34b9971 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -248,7 +248,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { .map(|origin| RegionDefinition::new(origin)) .collect(); - let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.nll_dump_cause); + let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.nll_dump_cause()); let mut result = Self { definitions, @@ -1259,7 +1259,7 @@ impl Cause { pub(crate) fn label_diagnostic(&self, mir: &Mir<'_>, diag: &mut DiagnosticBuilder<'_>) { // The cause information is pretty messy. Only dump it as an // internal debugging aid if -Znll-dump-cause is given. - let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.nll_dump_cause); + let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.nll_dump_cause()); if !nll_dump_cause { return; } diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 7cd92de88ee52..38227bd713322 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -509,7 +509,7 @@ impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> { o: Origin) -> DiagnosticBuilder<'a> { - if !o.should_emit_errors(self.sess.opts.borrowck_mode) { + if !o.should_emit_errors(self.sess.borrowck_mode()) { self.sess.diagnostic().cancel(&mut diag); } diag diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 97352783db85a..64063ec5beda9 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -124,7 +124,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_body(body); rcx.visit_region_obligations(id); } - rcx.resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors_unless_nll(); assert!(self.tables.borrow().free_region_map.is_empty()); self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); @@ -173,7 +173,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors_unless_nll(); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes @@ -371,6 +371,12 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { &self.outlives_environment); } + fn resolve_regions_and_report_errors_unless_nll(&self) { + self.fcx.resolve_regions_and_report_errors_unless_nll(self.subject_def_id, + &self.region_scope_tree, + &self.outlives_environment); + } + fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { debug!("regionck::visit_pat(pat={:?})", pat); pat.each_binding(|_, id, span, _| { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ba534676324a9..379881302eef7 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -186,6 +186,9 @@ declare_features! ( // Allows the use of rustc_* attributes; RFC 572 (active, rustc_attrs, "1.0.0", Some(29642)), + // Allows the use of non lexical lifetimes; RFC 2094 + (active, nll, "1.0.0", Some(44928)), + // Allows the use of #[allow_internal_unstable]. This is an // attribute on macro_rules! and can't use the attribute handling // below (it has to be checked before expansion possibly makes @@ -798,6 +801,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG libcore functions that are inlined \ across crates and will never be stable", cfg_fn!(rustc_attrs))), + + // RFC #2094 + ("nll", Whitelisted, Gated(Stability::Unstable, + "nll", + "Non lexical lifetimes", + cfg_fn!(nll))), ("compiler_builtins", Whitelisted, Gated(Stability::Unstable, "compiler_builtins", "the `#[compiler_builtins]` attribute is used to \ diff --git a/src/test/compile-fail/regions-struct-not-wf.rs b/src/test/compile-fail/regions-struct-not-wf.rs index a7f1828970324..9106f1f0ba69c 100644 --- a/src/test/compile-fail/regions-struct-not-wf.rs +++ b/src/test/compile-fail/regions-struct-not-wf.rs @@ -10,11 +10,15 @@ // Various examples of structs whose fields are not well-formed. +// revisions:lexical nll + #![allow(dead_code)] +#![cfg_attr(nll, feature(nll))] struct Ref<'a, T> { field: &'a T - //~^ ERROR the parameter type `T` may not live long enough + //[lexical]~^ ERROR the parameter type `T` may not live long enough + //[nll]~^^ ERROR the parameter type `T` may not live long enough } struct RefOk<'a, T:'a> { @@ -23,12 +27,14 @@ struct RefOk<'a, T:'a> { struct RefIndirect<'a, T> { field: RefOk<'a, T> - //~^ ERROR the parameter type `T` may not live long enough + //[lexical]~^ ERROR the parameter type `T` may not live long enough + //[nll]~^^ ERROR the parameter type `T` may not live long enough } struct DoubleRef<'a, 'b, T> { field: &'a &'b T - //~^ ERROR reference has a longer lifetime than the data it references + //[lexical]~^ ERROR reference has a longer lifetime than the data it references + //[nll]~^^ ERROR reference has a longer lifetime than the data it references } fn main() { } diff --git a/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs b/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs index a891e6072a7a6..35a5422040a17 100644 --- a/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs +++ b/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs @@ -10,7 +10,8 @@ // revisions: lxl nll //[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows -//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll + +#![cfg_attr(nll, feature(nll))] fn main() { let mut a = 0; diff --git a/src/test/run-pass/nll/get_default.rs b/src/test/run-pass/nll/get_default.rs new file mode 100644 index 0000000000000..13ef907d8d008 --- /dev/null +++ b/src/test/run-pass/nll/get_default.rs @@ -0,0 +1,31 @@ +// Copyright 2016 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(nll)] + +use std::collections::HashMap; + +fn get_default(map: &mut HashMap, key: usize) -> &mut String { + match map.get_mut(&key) { + Some(value) => value, + None => { + map.insert(key, "".to_string()); + map.get_mut(&key).unwrap() + } + } +} + +fn main() { + let map = &mut HashMap::new(); + map.insert(22, format!("Hello, world")); + map.insert(44, format!("Goodbye, world")); + assert_eq!(&*get_default(map, 22), "Hello, world"); + assert_eq!(&*get_default(map, 66), ""); +} diff --git a/src/test/run-pass/nll/process_or_insert_default.rs b/src/test/run-pass/nll/process_or_insert_default.rs new file mode 100644 index 0000000000000..a3a484402cc14 --- /dev/null +++ b/src/test/run-pass/nll/process_or_insert_default.rs @@ -0,0 +1,37 @@ +// Copyright 2016 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(nll)] + +use std::collections::HashMap; + +fn process_or_insert_default(map: &mut HashMap, key: usize) { + match map.get_mut(&key) { + Some(value) => { + process(value); + } + None => { + map.insert(key, "".to_string()); + } + } +} + +fn process(x: &str) { + assert_eq!(x, "Hello, world"); +} + +fn main() { + let map = &mut HashMap::new(); + map.insert(22, format!("Hello, world")); + map.insert(44, format!("Goodbye, world")); + process_or_insert_default(map, 22); + process_or_insert_default(map, 66); + assert_eq!(map[&66], ""); +} diff --git a/src/test/run-pass/nll/rc-loop.rs b/src/test/run-pass/nll/rc-loop.rs new file mode 100644 index 0000000000000..2b746fac4d426 --- /dev/null +++ b/src/test/run-pass/nll/rc-loop.rs @@ -0,0 +1,40 @@ +// Copyright 2016 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. + +// A test for something that NLL enables. It sometimes happens that +// the `while let` pattern makes some borrows from a variable (in this +// case, `x`) that you need in order to compute the next value for +// `x`. The lexical checker makes this very painful. The NLL checker +// does not. + +#![feature(match_default_bindings)] +#![feature(nll)] + +use std::rc::Rc; + +#[derive(Debug, PartialEq, Eq)] +enum Foo { + Base(usize), + Next(Rc), +} + +fn find_base(mut x: Rc) -> Rc { + while let Foo::Next(n) = &*x { + x = n.clone(); + } + x +} + +fn main() { + let chain = Rc::new(Foo::Next(Rc::new(Foo::Base(44)))); + let base = find_base(chain); + assert_eq!(&*base, &Foo::Base(44)); +} + diff --git a/src/test/ui/feature-gate-nll.rs b/src/test/ui/feature-gate-nll.rs new file mode 100644 index 0000000000000..f34a9cddf98e4 --- /dev/null +++ b/src/test/ui/feature-gate-nll.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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. + +#![allow(dead_code)] + +fn main() { + let mut x = 33; + + let p = &x; + x = 22; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/feature-gate-nll.stderr b/src/test/ui/feature-gate-nll.stderr new file mode 100644 index 0000000000000..4135462305a89 --- /dev/null +++ b/src/test/ui/feature-gate-nll.stderr @@ -0,0 +1,10 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/feature-gate-nll.rs:17:5 + | +16 | let p = &x; + | - borrow of `x` occurs here +17 | x = 22; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/capture-ref-in-struct.rs b/src/test/ui/nll/capture-ref-in-struct.rs index 6e234a966d14a..74b086ab18a58 100644 --- a/src/test/ui/nll/capture-ref-in-struct.rs +++ b/src/test/ui/nll/capture-ref-in-struct.rs @@ -8,12 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause +// compile-flags:-Znll-dump-cause // Test that a structure which tries to store a pointer to `y` into // `p` (indirectly) fails to compile. #![feature(rustc_attrs)] +#![feature(nll)] struct SomeStruct<'a, 'b: 'a> { p: &'a mut &'b i32, diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr index 7b3f7d25dd037..7e7487daa67a3 100644 --- a/src/test/ui/nll/capture-ref-in-struct.stderr +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -1,13 +1,13 @@ error[E0597]: `y` does not live long enough - --> $DIR/capture-ref-in-struct.rs:32:16 + --> $DIR/capture-ref-in-struct.rs:33:16 | -32 | y: &y, +33 | y: &y, | ^^ borrowed value does not live long enough ... -37 | } +38 | } | - borrowed value only lives until here -38 | -39 | deref(p); +39 | +40 | deref(p); | - borrow later used here | = note: borrowed value must be valid for lifetime '_#5r... diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.rs new file mode 100644 index 0000000000000..babe608354fb9 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.rs @@ -0,0 +1,41 @@ +// Copyright 2016 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 we assume that universal types like `T` outlive the +// function body. Same as ty-param-fn-body, but uses `feature(nll)`, +// which affects error reporting. + +#![feature(nll)] + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::cell::Cell; + +// No errors here, because `'a` is local to the body. +fn region_within_body(t: T) { + let some_int = 22; + let cell = Cell::new(&some_int); + outlives(cell, t) +} + +// Error here, because T: 'a is not satisfied. +fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { + outlives(cell, t) + //~^ ERROR the parameter type `T` may not live long enough +} + +fn outlives<'a, T>(x: Cell<&'a usize>, y: T) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.stderr new file mode 100644 index 0000000000000..fa9105df07027 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.stderr @@ -0,0 +1,10 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-fn-body-nll-feature.rs:31:5 + | +31 | outlives(cell, t) + | ^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... + +error: aborting due to previous error +