Auto merge of #52681 - pnkfelix:z-borrowck-migrate, r=nikomatsakis
Add `-Z borrowck=migrate` This adds `-Z borrowck=migrate`, which represents the way we want to migrate to NLL under Rust versions to come. It also hooks this new mode into `--edition 2018`, which means we're officially turning NLL on in the 2018 edition. The basic idea of `-Z borrowck=migrate` that there are cases where NLL is fixing old soundness bugs in the borrow-checker, but in order to avoid just breaking code by immediately rejecting the programs that hit those soundness bugs, we instead use the following strategy: If your code is accepted by NLL, then we accept it. If your code is rejected by both NLL and the old AST-borrowck, then we reject it. If your code is rejected by NLL but accepted by the old AST-borrowck, then we emit the new NLL errors as **warnings**. These warnings will be turned into hard errors in the future, and they say so in these diagnostics. Fix #46908
This commit is contained in:
commit
b18b9edf00
@ -15,9 +15,15 @@ use util::nodemap::FxHashSet;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
|
||||
StableHasherResult};
|
||||
|
||||
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub enum SignalledError { SawSomeError, NoErrorsSeen }
|
||||
|
||||
impl_stable_hash_for!(enum self::SignalledError { SawSomeError, NoErrorsSeen });
|
||||
|
||||
#[derive(Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct BorrowCheckResult {
|
||||
pub used_mut_nodes: FxHashSet<HirId>,
|
||||
pub signalled_any_error: SignalledError,
|
||||
}
|
||||
|
||||
impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult {
|
||||
@ -26,7 +32,9 @@ impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult {
|
||||
hasher: &mut StableHasher<W>) {
|
||||
let BorrowCheckResult {
|
||||
ref used_mut_nodes,
|
||||
ref signalled_any_error,
|
||||
} = *self;
|
||||
used_mut_nodes.hash_stable(hcx, hasher);
|
||||
signalled_any_error.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
@ -455,15 +455,28 @@ pub enum BorrowckMode {
|
||||
Ast,
|
||||
Mir,
|
||||
Compare,
|
||||
Migrate,
|
||||
}
|
||||
|
||||
impl BorrowckMode {
|
||||
/// Should we run the MIR-based borrow check, but also fall back
|
||||
/// on the AST borrow check if the MIR-based one errors.
|
||||
pub fn migrate(self) -> bool {
|
||||
match self {
|
||||
BorrowckMode::Ast => false,
|
||||
BorrowckMode::Compare => false,
|
||||
BorrowckMode::Mir => false,
|
||||
BorrowckMode::Migrate => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Should we emit the AST-based borrow checker errors?
|
||||
pub fn use_ast(self) -> bool {
|
||||
match self {
|
||||
BorrowckMode::Ast => true,
|
||||
BorrowckMode::Compare => true,
|
||||
BorrowckMode::Mir => false,
|
||||
BorrowckMode::Migrate => false,
|
||||
}
|
||||
}
|
||||
/// Should we emit the MIR-based borrow checker errors?
|
||||
@ -472,6 +485,7 @@ impl BorrowckMode {
|
||||
BorrowckMode::Ast => false,
|
||||
BorrowckMode::Compare => true,
|
||||
BorrowckMode::Mir => true,
|
||||
BorrowckMode::Migrate => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1127,7 +1141,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
emit_end_regions: bool = (false, parse_bool, [UNTRACKED],
|
||||
"emit EndRegion as part of MIR; enable transforms that solely process EndRegion"),
|
||||
borrowck: Option<String> = (None, parse_opt_string, [UNTRACKED],
|
||||
"select which borrowck is used (`ast`, `mir`, or `compare`)"),
|
||||
"select which borrowck is used (`ast`, `mir`, `migrate`, or `compare`)"),
|
||||
two_phase_borrows: bool = (false, parse_bool, [UNTRACKED],
|
||||
"use two-phase reserved/active distinction for `&mut` borrows in MIR borrowck"),
|
||||
two_phase_beyond_autoref: bool = (false, parse_bool, [UNTRACKED],
|
||||
@ -2168,6 +2182,7 @@ pub fn build_session_options_and_crate_config(
|
||||
None | Some("ast") => BorrowckMode::Ast,
|
||||
Some("mir") => BorrowckMode::Mir,
|
||||
Some("compare") => BorrowckMode::Compare,
|
||||
Some("migrate") => BorrowckMode::Migrate,
|
||||
Some(m) => early_error(error_format, &format!("unknown borrowck mode `{}`", m)),
|
||||
};
|
||||
|
||||
|
@ -74,6 +74,7 @@ use rustc_target::spec::abi;
|
||||
use syntax::ast::{self, NodeId};
|
||||
use syntax::attr;
|
||||
use syntax::codemap::MultiSpan;
|
||||
use syntax::edition::Edition;
|
||||
use syntax::feature_gate;
|
||||
use syntax::symbol::{Symbol, keywords, InternedString};
|
||||
use syntax_pos::Span;
|
||||
@ -1366,6 +1367,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
self.borrowck_mode().use_mir()
|
||||
}
|
||||
|
||||
/// If true, we should use the MIR-based borrow check, but also
|
||||
/// fall back on the AST borrow check if the MIR-based one errors.
|
||||
pub fn migrate_borrowck(self) -> bool {
|
||||
self.borrowck_mode().migrate()
|
||||
}
|
||||
|
||||
/// If true, make MIR codegen for `match` emit a temp that holds a
|
||||
/// borrow of the input to the match expression.
|
||||
pub fn generate_borrow_of_any_match_input(&self) -> bool {
|
||||
@ -1397,18 +1404,51 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
/// What mode(s) of borrowck should we run? AST? MIR? both?
|
||||
/// (Also considers the `#![feature(nll)]` setting.)
|
||||
pub fn borrowck_mode(&self) -> BorrowckMode {
|
||||
// Here are the main constraints we need to deal with:
|
||||
//
|
||||
// 1. An opts.borrowck_mode of `BorrowckMode::Ast` is
|
||||
// synonymous with no `-Z borrowck=...` flag at all.
|
||||
// (This is arguably a historical accident.)
|
||||
//
|
||||
// 2. `BorrowckMode::Migrate` is the limited migration to
|
||||
// NLL that we are deploying with the 2018 edition.
|
||||
//
|
||||
// 3. We want to allow developers on the Nightly channel
|
||||
// to opt back into the "hard error" mode for NLL,
|
||||
// (which they can do via specifying `#![feature(nll)]`
|
||||
// explicitly in their crate).
|
||||
//
|
||||
// So, this precedence list is how pnkfelix chose to work with
|
||||
// the above constraints:
|
||||
//
|
||||
// * `#![feature(nll)]` *always* means use NLL with hard
|
||||
// errors. (To simplify the code here, it now even overrides
|
||||
// a user's attempt to specify `-Z borrowck=compare`, which
|
||||
// we arguably do not need anymore and should remove.)
|
||||
//
|
||||
// * Otherwise, if no `-Z borrowck=...` flag was given (or
|
||||
// if `borrowck=ast` was specified), then use the default
|
||||
// as required by the edition.
|
||||
//
|
||||
// * Otherwise, use the behavior requested via `-Z borrowck=...`
|
||||
|
||||
if self.features().nll { return BorrowckMode::Mir; }
|
||||
|
||||
match self.sess.opts.borrowck_mode {
|
||||
mode @ BorrowckMode::Mir |
|
||||
mode @ BorrowckMode::Compare => mode,
|
||||
mode @ BorrowckMode::Compare |
|
||||
mode @ BorrowckMode::Migrate => mode,
|
||||
|
||||
mode @ BorrowckMode::Ast => {
|
||||
if self.features().nll {
|
||||
BorrowckMode::Mir
|
||||
} else {
|
||||
mode
|
||||
}
|
||||
}
|
||||
BorrowckMode::Ast => match self.sess.edition() {
|
||||
Edition::Edition2015 => BorrowckMode::Ast,
|
||||
Edition::Edition2018 => BorrowckMode::Migrate,
|
||||
|
||||
// For now, future editions mean Migrate. (But it
|
||||
// would make a lot of sense for it to be changed to
|
||||
// `BorrowckMode::Mir`, depending on how we plan to
|
||||
// time the forcing of full migration to NLL.)
|
||||
_ => BorrowckMode::Migrate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,10 +447,12 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
||||
.region_scope_tree
|
||||
.yield_in_scope_for_expr(scope,
|
||||
cmt.hir_id,
|
||||
self.bccx.body) {
|
||||
self.bccx.body)
|
||||
{
|
||||
self.bccx.cannot_borrow_across_generator_yield(borrow_span,
|
||||
yield_span,
|
||||
Origin::Ast).emit();
|
||||
self.bccx.signal_error();
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,9 +509,13 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
||||
new_loan, old_loan, old_loan, new_loan).err();
|
||||
|
||||
match (err_old_new, err_new_old) {
|
||||
(Some(mut err), None) | (None, Some(mut err)) => err.emit(),
|
||||
(Some(mut err), None) | (None, Some(mut err)) => {
|
||||
err.emit();
|
||||
self.bccx.signal_error();
|
||||
}
|
||||
(Some(mut err_old), Some(mut err_new)) => {
|
||||
err_old.emit();
|
||||
self.bccx.signal_error();
|
||||
err_new.cancel();
|
||||
}
|
||||
(None, None) => return true,
|
||||
@ -695,6 +701,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
||||
loan_span, &self.bccx.loan_path_to_string(&loan_path),
|
||||
Origin::Ast)
|
||||
.emit();
|
||||
self.bccx.signal_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -745,6 +752,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
err.emit();
|
||||
self.bccx.signal_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -914,5 +922,6 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
||||
self.bccx.cannot_assign_to_borrowed(
|
||||
span, loan.span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast)
|
||||
.emit();
|
||||
self.bccx.signal_error();
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec<Move
|
||||
"captured outer variable");
|
||||
}
|
||||
err.emit();
|
||||
bccx.signal_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ use rustc::middle::dataflow::DataFlowContext;
|
||||
use rustc::middle::dataflow::BitwiseOperator;
|
||||
use rustc::middle::dataflow::DataFlowOperator;
|
||||
use rustc::middle::dataflow::KillFrom;
|
||||
use rustc::middle::borrowck::BorrowCheckResult;
|
||||
use rustc::middle::borrowck::{BorrowCheckResult, SignalledError};
|
||||
use rustc::hir::def_id::{DefId, LocalDefId};
|
||||
use rustc::middle::expr_use_visitor as euv;
|
||||
use rustc::middle::mem_categorization as mc;
|
||||
@ -42,7 +42,7 @@ use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
|
||||
use rustc_mir::util::suggest_ref_mut;
|
||||
use rustc::util::nodemap::FxHashSet;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
@ -90,7 +90,7 @@ pub struct AnalysisData<'a, 'tcx: 'a> {
|
||||
fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
|
||||
-> Lrc<BorrowCheckResult>
|
||||
{
|
||||
assert!(tcx.use_ast_borrowck());
|
||||
assert!(tcx.use_ast_borrowck() || tcx.migrate_borrowck());
|
||||
|
||||
debug!("borrowck(body_owner_def_id={:?})", owner_def_id);
|
||||
|
||||
@ -105,6 +105,7 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
|
||||
// and do not need borrowchecking.
|
||||
return Lrc::new(BorrowCheckResult {
|
||||
used_mut_nodes: FxHashSet(),
|
||||
signalled_any_error: SignalledError::NoErrorsSeen,
|
||||
})
|
||||
}
|
||||
_ => { }
|
||||
@ -121,6 +122,7 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
|
||||
owner_def_id,
|
||||
body,
|
||||
used_mut_nodes: RefCell::new(FxHashSet()),
|
||||
signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
|
||||
};
|
||||
|
||||
// Eventually, borrowck will always read the MIR, but at the
|
||||
@ -154,6 +156,7 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
|
||||
|
||||
Lrc::new(BorrowCheckResult {
|
||||
used_mut_nodes: bccx.used_mut_nodes.into_inner(),
|
||||
signalled_any_error: bccx.signalled_any_error.into_inner(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -234,6 +237,7 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
|
||||
owner_def_id,
|
||||
body,
|
||||
used_mut_nodes: RefCell::new(FxHashSet()),
|
||||
signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
|
||||
};
|
||||
|
||||
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
|
||||
@ -257,6 +261,15 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
|
||||
body: &'tcx hir::Body,
|
||||
|
||||
used_mut_nodes: RefCell<FxHashSet<HirId>>,
|
||||
|
||||
signalled_any_error: Cell<SignalledError>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a, 'tcx: 'a> BorrowckCtxt<'a, 'tcx> {
|
||||
fn signal_error(&self) {
|
||||
self.signalled_any_error.set(SignalledError::SawSomeError);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx: 'b> BorrowckErrors<'a> for &'a BorrowckCtxt<'b, 'tcx> {
|
||||
@ -645,6 +658,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
.span_label(use_span, format!("use of possibly uninitialized `{}`",
|
||||
self.loan_path_to_string(lp)))
|
||||
.emit();
|
||||
self.signal_error();
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
@ -760,6 +774,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
// not considered particularly helpful.
|
||||
|
||||
err.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
|
||||
pub fn report_partial_reinitialization_of_uninitialized_structure(
|
||||
@ -770,6 +785,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
&self.loan_path_to_string(lp),
|
||||
Origin::Ast)
|
||||
.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
|
||||
pub fn report_reassigned_immutable_variable(&self,
|
||||
@ -787,6 +803,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
self.loan_path_to_string(lp)));
|
||||
}
|
||||
err.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
|
||||
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self,
|
||||
@ -908,6 +925,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
self.tcx.hir.hir_to_node_id(err.cmt.hir_id)
|
||||
);
|
||||
db.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
err_out_of_scope(super_scope, sub_scope, cause) => {
|
||||
let msg = match opt_loan_path(&err.cmt) {
|
||||
@ -1022,6 +1040,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
db.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
|
||||
let descr = self.cmt_to_path_or_string(err.cmt);
|
||||
@ -1047,6 +1066,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
"");
|
||||
|
||||
db.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1125,6 +1145,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
err.help("closures behind references must be called via `&mut`");
|
||||
}
|
||||
err.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
|
||||
/// Given a type, if it is an immutable reference, return a suggestion to make it mutable
|
||||
@ -1307,6 +1328,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
cmt_path_or_string),
|
||||
suggestion)
|
||||
.emit();
|
||||
self.signal_error();
|
||||
}
|
||||
|
||||
fn region_end_span(&self, region: ty::Region<'tcx>) -> Option<Span> {
|
||||
|
@ -99,6 +99,25 @@ impl Diagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_error(&self) -> bool {
|
||||
match self.level {
|
||||
Level::Bug |
|
||||
Level::Fatal |
|
||||
Level::PhaseFatal |
|
||||
Level::Error |
|
||||
Level::FailureNote => {
|
||||
true
|
||||
}
|
||||
|
||||
Level::Warning |
|
||||
Level::Note |
|
||||
Level::Help |
|
||||
Level::Cancelled => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel the diagnostic (a structured diagnostic must either be emitted or
|
||||
/// canceled or it will panic when dropped).
|
||||
pub fn cancel(&mut self) {
|
||||
|
@ -100,25 +100,6 @@ impl<'a> DiagnosticBuilder<'a> {
|
||||
buffered_diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
pub fn is_error(&self) -> bool {
|
||||
match self.level {
|
||||
Level::Bug |
|
||||
Level::Fatal |
|
||||
Level::PhaseFatal |
|
||||
Level::Error |
|
||||
Level::FailureNote => {
|
||||
true
|
||||
}
|
||||
|
||||
Level::Warning |
|
||||
Level::Note |
|
||||
Level::Help |
|
||||
Level::Cancelled => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// span_* methods instead.
|
||||
pub fn sub<S: Into<MultiSpan>>(
|
||||
|
@ -16,6 +16,7 @@ use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::lint::builtin::UNUSED_MUT;
|
||||
use rustc::middle::borrowck::SignalledError;
|
||||
use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
|
||||
use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Place};
|
||||
use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
|
||||
@ -23,7 +24,7 @@ use rustc::mir::{Terminator, TerminatorKind};
|
||||
use rustc::ty::query::Providers;
|
||||
use rustc::ty::{self, ParamEnv, TyCtxt};
|
||||
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder};
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder, Level};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::indexed_set::IdxSetBuf;
|
||||
@ -329,8 +330,34 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
for diag in mbcx.errors_buffer.drain(..) {
|
||||
DiagnosticBuilder::new_diagnostic(mbcx.tcx.sess.diagnostic(), diag).emit();
|
||||
if mbcx.errors_buffer.len() > 0 {
|
||||
if tcx.migrate_borrowck() {
|
||||
match tcx.borrowck(def_id).signalled_any_error {
|
||||
SignalledError::NoErrorsSeen => {
|
||||
// if AST-borrowck signalled no errors, then
|
||||
// downgrade all the buffered MIR-borrowck errors
|
||||
// to warnings.
|
||||
for err in &mut mbcx.errors_buffer {
|
||||
if err.is_error() {
|
||||
err.level = Level::Warning;
|
||||
err.warn("This error has been downgraded to a warning \
|
||||
for backwards compatibility with previous releases.\n\
|
||||
It represents potential unsoundness in your code.\n\
|
||||
This warning will become a hard error in the future.");
|
||||
}
|
||||
}
|
||||
}
|
||||
SignalledError::SawSomeError => {
|
||||
// if AST-borrowck signalled a (cancelled) error,
|
||||
// then we will just emit the buffered
|
||||
// MIR-borrowck errors as normal.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for diag in mbcx.errors_buffer.drain(..) {
|
||||
DiagnosticBuilder::new_diagnostic(mbcx.tcx.sess.diagnostic(), diag).emit();
|
||||
}
|
||||
}
|
||||
|
||||
let result = BorrowCheckResult {
|
||||
@ -1747,20 +1774,44 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
Reservation(WriteKind::Move)
|
||||
| Write(WriteKind::Move)
|
||||
| Reservation(WriteKind::StorageDeadOrDrop)
|
||||
| Reservation(WriteKind::MutableBorrow(BorrowKind::Shared))
|
||||
| Write(WriteKind::StorageDeadOrDrop)
|
||||
| Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
|
||||
Reservation(wk @ WriteKind::Move)
|
||||
| Write(wk @ WriteKind::Move)
|
||||
| Reservation(wk @ WriteKind::StorageDeadOrDrop)
|
||||
| Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
|
||||
| Write(wk @ WriteKind::StorageDeadOrDrop)
|
||||
| Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => {
|
||||
if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
&format!(
|
||||
"Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
|
||||
place, kind
|
||||
),
|
||||
);
|
||||
if self.tcx.migrate_borrowck() {
|
||||
// rust-lang/rust#46908: In pure NLL mode this
|
||||
// code path should be unreachable (and thus
|
||||
// we signal an ICE in the else branch
|
||||
// here). But we can legitimately get here
|
||||
// under borrowck=migrate mode, so instead of
|
||||
// ICE'ing we instead report a legitimate
|
||||
// error (which will then be downgraded to a
|
||||
// warning by the migrate machinery).
|
||||
error_access = match wk {
|
||||
WriteKind::MutableBorrow(_) => AccessKind::MutableBorrow,
|
||||
WriteKind::Move => AccessKind::Move,
|
||||
WriteKind::StorageDeadOrDrop |
|
||||
WriteKind::Mutate => AccessKind::Mutate,
|
||||
};
|
||||
self.report_mutability_error(
|
||||
place,
|
||||
span,
|
||||
_place_err,
|
||||
error_access,
|
||||
location,
|
||||
);
|
||||
} else {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
&format!(
|
||||
"Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
|
||||
place, kind
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use util::suggest_ref_mut;
|
||||
pub(super) enum AccessKind {
|
||||
MutableBorrow,
|
||||
Mutate,
|
||||
Move,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
||||
@ -110,6 +111,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
||||
if let Some(desc) = access_place_desc {
|
||||
item_msg = format!("`{}`", desc);
|
||||
reason = match error_access {
|
||||
AccessKind::Move |
|
||||
AccessKind::Mutate => format!(" which is behind a {}", pointer_type),
|
||||
AccessKind::MutableBorrow => {
|
||||
format!(", as it is behind a {}", pointer_type)
|
||||
@ -160,6 +162,13 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
|
||||
let span = match error_access {
|
||||
AccessKind::Move => {
|
||||
err = self.tcx
|
||||
.cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir);
|
||||
act = "move";
|
||||
acted_on = "moved";
|
||||
span
|
||||
}
|
||||
AccessKind::Mutate => {
|
||||
err = self.tcx
|
||||
.cannot_assign(span, &(item_msg + &reason), Origin::Mir);
|
||||
|
@ -41,7 +41,20 @@ impl MirPass for ElaborateDrops {
|
||||
|
||||
let id = tcx.hir.as_local_node_id(src.def_id).unwrap();
|
||||
let param_env = tcx.param_env(src.def_id).with_reveal_all();
|
||||
let move_data = MoveData::gather_moves(mir, tcx).unwrap();
|
||||
let move_data = match MoveData::gather_moves(mir, tcx) {
|
||||
Ok(move_data) => move_data,
|
||||
Err((move_data, _move_errors)) => {
|
||||
// The only way we should be allowing any move_errors
|
||||
// in here is if we are in the migration path for the
|
||||
// NLL-based MIR-borrowck.
|
||||
//
|
||||
// If we are in the migration path, we have already
|
||||
// reported these errors as warnings to the user. So
|
||||
// we will just ignore them here.
|
||||
assert!(tcx.migrate_borrowck());
|
||||
move_data
|
||||
}
|
||||
};
|
||||
let elaborate_patch = {
|
||||
let mir = &*mir;
|
||||
let env = MoveDataParamEnv {
|
||||
|
@ -0,0 +1,9 @@
|
||||
error[E0507]: cannot move out of borrowed content
|
||||
--> $DIR/borrowck-feature-nll-overrides-migrate.rs:32:17
|
||||
|
|
||||
LL | (|| { let bar = foo; bar.take() })();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0507`.
|
@ -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 <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.
|
||||
|
||||
// This is a test that the `#![feature(nll)]` opt-in overrides the
|
||||
// migration mode. The intention here is to emulate the goal behavior
|
||||
// that `--edition 2018` effects on borrowck (modeled here by `-Z
|
||||
// borrowck=migrate`) are themselves overridden by the
|
||||
// `#![feature(nll)]` opt-in.
|
||||
//
|
||||
// Therefore, for developer convenience, under `#[feature(nll)]` the
|
||||
// NLL checks will be emitted as errors *even* in the presence of `-Z
|
||||
// borrowck=migrate`.
|
||||
|
||||
// revisions: zflag edition
|
||||
// [zflag]compile-flags: -Z borrowck=migrate
|
||||
// [edition]compile-flags: --edition 2018
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
fn main() {
|
||||
match Some(&4) {
|
||||
None => {},
|
||||
ref mut foo
|
||||
if {
|
||||
(|| { let bar = foo; bar.take() })();
|
||||
//[zflag]~^ ERROR cannot move out of borrowed content [E0507]
|
||||
//[edition]~^^ ERROR cannot move out of borrowed content [E0507]
|
||||
false
|
||||
} => {},
|
||||
Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."),
|
||||
_ => println!("Here is some supposedly unreachable code."),
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
error[E0507]: cannot move out of borrowed content
|
||||
--> $DIR/borrowck-feature-nll-overrides-migrate.rs:32:17
|
||||
|
|
||||
LL | (|| { let bar = foo; bar.take() })();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0507`.
|
24
src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr
Normal file
24
src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr
Normal file
@ -0,0 +1,24 @@
|
||||
warning[E0507]: cannot move out of borrowed content
|
||||
--> $DIR/borrowck-migrate-to-nll.rs:35:17
|
||||
|
|
||||
LL | (|| { let bar = foo; bar.take() })();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
|
||||
|
|
||||
= warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
|
||||
It represents potential unsoundness in your code.
|
||||
This warning will become a hard error in the future.
|
||||
|
||||
warning[E0507]: cannot move out of `foo`, as it is immutable for the pattern guard
|
||||
--> $DIR/borrowck-migrate-to-nll.rs:35:17
|
||||
|
|
||||
LL | (|| { let bar = foo; bar.take() })();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| cannot move out of `foo`, as it is immutable for the pattern guard
|
||||
| cannot move
|
||||
|
|
||||
= note: variables bound in patterns are immutable until the end of the pattern guard
|
||||
= warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
|
||||
It represents potential unsoundness in your code.
|
||||
This warning will become a hard error in the future.
|
||||
|
41
src/test/ui/borrowck/borrowck-migrate-to-nll.rs
Normal file
41
src/test/ui/borrowck/borrowck-migrate-to-nll.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// This is a test of the borrowck migrate mode. It leverages #27282, a
|
||||
// bug that is fixed by NLL: this code is (unsoundly) accepted by
|
||||
// AST-borrowck, but is correctly rejected by the NLL borrowck.
|
||||
//
|
||||
// Therefore, for backwards-compatiblity, under borrowck=migrate the
|
||||
// NLL checks will be emitted as *warnings*.
|
||||
|
||||
// NLL mode makes this compile-fail; we cannot currently encode a
|
||||
// test that is run-pass or compile-fail based on compare-mode. So
|
||||
// just ignore it instead:
|
||||
|
||||
// ignore-compare-mode-nll
|
||||
|
||||
// revisions: zflag edition
|
||||
//[zflag]compile-flags: -Z borrowck=migrate
|
||||
//[edition]compile-flags: --edition 2018
|
||||
//[zflag] run-pass
|
||||
//[edition] run-pass
|
||||
|
||||
fn main() {
|
||||
match Some(&4) {
|
||||
None => {},
|
||||
ref mut foo
|
||||
if {
|
||||
(|| { let bar = foo; bar.take() })();
|
||||
false
|
||||
} => {},
|
||||
Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."),
|
||||
_ => println!("Here is some supposedly unreachable code."),
|
||||
}
|
||||
}
|
24
src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr
Normal file
24
src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr
Normal file
@ -0,0 +1,24 @@
|
||||
warning[E0507]: cannot move out of borrowed content
|
||||
--> $DIR/borrowck-migrate-to-nll.rs:35:17
|
||||
|
|
||||
LL | (|| { let bar = foo; bar.take() })();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
|
||||
|
|
||||
= warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
|
||||
It represents potential unsoundness in your code.
|
||||
This warning will become a hard error in the future.
|
||||
|
||||
warning[E0507]: cannot move out of `foo`, as it is immutable for the pattern guard
|
||||
--> $DIR/borrowck-migrate-to-nll.rs:35:17
|
||||
|
|
||||
LL | (|| { let bar = foo; bar.take() })();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| cannot move out of `foo`, as it is immutable for the pattern guard
|
||||
| cannot move
|
||||
|
|
||||
= note: variables bound in patterns are immutable until the end of the pattern guard
|
||||
= warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
|
||||
It represents potential unsoundness in your code.
|
||||
This warning will become a hard error in the future.
|
||||
|
@ -8,17 +8,15 @@
|
||||
// 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)
|
||||
let _a = &mut true;
|
||||
let b = &mut true;
|
||||
//~^ borrow may still be in use when generator yields
|
||||
yield ();
|
||||
println!("{}", b);
|
||||
};
|
||||
|
@ -1,30 +1,12 @@
|
||||
error[E0626]: borrow may still be in use when generator yields (Ast)
|
||||
--> $DIR/generator-with-nll.rs:19:23
|
||||
error[E0626]: borrow may still be in use when generator yields
|
||||
--> $DIR/generator-with-nll.rs:18:17
|
||||
|
|
||||
LL | let _a = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
|
||||
| ^^^^
|
||||
...
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
||||
error[E0626]: borrow may still be in use when generator yields (Ast)
|
||||
--> $DIR/generator-with-nll.rs:20:22
|
||||
|
|
||||
LL | let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
|
||||
| ^^^^
|
||||
LL | //~^ borrow may still be in use when generator yields (Mir)
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
||||
error[E0626]: borrow may still be in use when generator yields (Mir)
|
||||
--> $DIR/generator-with-nll.rs:20:17
|
||||
|
|
||||
LL | let b = &mut true; //~ ERROR borrow may still be in use when generator yields (Ast)
|
||||
LL | let b = &mut true;
|
||||
| ^^^^^^^^^
|
||||
LL | //~^ borrow may still be in use when generator yields (Mir)
|
||||
LL | //~^ borrow may still be in use when generator yields
|
||||
LL | yield ();
|
||||
| -------- possible yield occurs here
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0626`.
|
||||
|
@ -14,7 +14,7 @@ use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use common::{self, Config, Mode};
|
||||
use common::{self, CompareMode, Config, Mode};
|
||||
use util;
|
||||
|
||||
use extract_gdb_version;
|
||||
@ -608,7 +608,12 @@ impl Config {
|
||||
common::DebugInfoLldb => name == "lldb",
|
||||
common::Pretty => name == "pretty",
|
||||
_ => false,
|
||||
} || (self.target != self.host && name == "cross-compile")
|
||||
} || (self.target != self.host && name == "cross-compile") ||
|
||||
match self.compare_mode {
|
||||
Some(CompareMode::Nll) => name == "compare-mode-nll",
|
||||
Some(CompareMode::Polonius) => name == "compare-mode-polonius",
|
||||
None => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user