Skip to content

Add feature(nll) #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: &region::ScopeTree,
errors: &Vec<RegionResolutionError<'tcx>>) {
errors: &Vec<RegionResolutionError<'tcx>>,
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, ..) |
Expand Down
45 changes: 40 additions & 5 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>) {
pub fn resolve_regions_and_report_errors(
&self,
region_context: DefId,
region_map: &region::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: &region::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: &region::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());
Expand All @@ -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);
}
}

Expand Down
57 changes: 54 additions & 3 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_borrowck/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 6 additions & 9 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/util/borrowck_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions src/librustc_typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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, _| {
Expand Down
9 changes: 9 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 \
Expand Down
12 changes: 9 additions & 3 deletions src/test/compile-fail/regions-struct-not-wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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() { }
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions src/test/run-pass/nll/get_default.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(nll)]

use std::collections::HashMap;

fn get_default(map: &mut HashMap<usize, String>, 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), "");
}
Loading