Skip to content

Commit b18b9ed

Browse files
committed
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
2 parents 7c2aeb9 + 9f05f29 commit b18b9ed

20 files changed

+382
-82
lines changed

src/librustc/middle/borrowck.rs

+8
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ use util::nodemap::FxHashSet;
1515
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
1616
StableHasherResult};
1717

18+
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
19+
pub enum SignalledError { SawSomeError, NoErrorsSeen }
20+
21+
impl_stable_hash_for!(enum self::SignalledError { SawSomeError, NoErrorsSeen });
22+
1823
#[derive(Debug, RustcEncodable, RustcDecodable)]
1924
pub struct BorrowCheckResult {
2025
pub used_mut_nodes: FxHashSet<HirId>,
26+
pub signalled_any_error: SignalledError,
2127
}
2228

2329
impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult {
@@ -26,7 +32,9 @@ impl<'a> HashStable<StableHashingContext<'a>> for BorrowCheckResult {
2632
hasher: &mut StableHasher<W>) {
2733
let BorrowCheckResult {
2834
ref used_mut_nodes,
35+
ref signalled_any_error,
2936
} = *self;
3037
used_mut_nodes.hash_stable(hcx, hasher);
38+
signalled_any_error.hash_stable(hcx, hasher);
3139
}
3240
}

src/librustc/session/config.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,28 @@ pub enum BorrowckMode {
455455
Ast,
456456
Mir,
457457
Compare,
458+
Migrate,
458459
}
459460

460461
impl BorrowckMode {
462+
/// Should we run the MIR-based borrow check, but also fall back
463+
/// on the AST borrow check if the MIR-based one errors.
464+
pub fn migrate(self) -> bool {
465+
match self {
466+
BorrowckMode::Ast => false,
467+
BorrowckMode::Compare => false,
468+
BorrowckMode::Mir => false,
469+
BorrowckMode::Migrate => true,
470+
}
471+
}
472+
461473
/// Should we emit the AST-based borrow checker errors?
462474
pub fn use_ast(self) -> bool {
463475
match self {
464476
BorrowckMode::Ast => true,
465477
BorrowckMode::Compare => true,
466478
BorrowckMode::Mir => false,
479+
BorrowckMode::Migrate => false,
467480
}
468481
}
469482
/// Should we emit the MIR-based borrow checker errors?
@@ -472,6 +485,7 @@ impl BorrowckMode {
472485
BorrowckMode::Ast => false,
473486
BorrowckMode::Compare => true,
474487
BorrowckMode::Mir => true,
488+
BorrowckMode::Migrate => true,
475489
}
476490
}
477491
}
@@ -1127,7 +1141,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
11271141
emit_end_regions: bool = (false, parse_bool, [UNTRACKED],
11281142
"emit EndRegion as part of MIR; enable transforms that solely process EndRegion"),
11291143
borrowck: Option<String> = (None, parse_opt_string, [UNTRACKED],
1130-
"select which borrowck is used (`ast`, `mir`, or `compare`)"),
1144+
"select which borrowck is used (`ast`, `mir`, `migrate`, or `compare`)"),
11311145
two_phase_borrows: bool = (false, parse_bool, [UNTRACKED],
11321146
"use two-phase reserved/active distinction for `&mut` borrows in MIR borrowck"),
11331147
two_phase_beyond_autoref: bool = (false, parse_bool, [UNTRACKED],
@@ -2168,6 +2182,7 @@ pub fn build_session_options_and_crate_config(
21682182
None | Some("ast") => BorrowckMode::Ast,
21692183
Some("mir") => BorrowckMode::Mir,
21702184
Some("compare") => BorrowckMode::Compare,
2185+
Some("migrate") => BorrowckMode::Migrate,
21712186
Some(m) => early_error(error_format, &format!("unknown borrowck mode `{}`", m)),
21722187
};
21732188

src/librustc/ty/context.rs

+50-10
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ use rustc_target::spec::abi;
7474
use syntax::ast::{self, NodeId};
7575
use syntax::attr;
7676
use syntax::codemap::MultiSpan;
77+
use syntax::edition::Edition;
7778
use syntax::feature_gate;
7879
use syntax::symbol::{Symbol, keywords, InternedString};
7980
use syntax_pos::Span;
@@ -1366,6 +1367,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
13661367
self.borrowck_mode().use_mir()
13671368
}
13681369

1370+
/// If true, we should use the MIR-based borrow check, but also
1371+
/// fall back on the AST borrow check if the MIR-based one errors.
1372+
pub fn migrate_borrowck(self) -> bool {
1373+
self.borrowck_mode().migrate()
1374+
}
1375+
13691376
/// If true, make MIR codegen for `match` emit a temp that holds a
13701377
/// borrow of the input to the match expression.
13711378
pub fn generate_borrow_of_any_match_input(&self) -> bool {
@@ -1397,18 +1404,51 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
13971404
/// What mode(s) of borrowck should we run? AST? MIR? both?
13981405
/// (Also considers the `#![feature(nll)]` setting.)
13991406
pub fn borrowck_mode(&self) -> BorrowckMode {
1400-
match self.sess.opts.borrowck_mode {
1401-
mode @ BorrowckMode::Mir |
1402-
mode @ BorrowckMode::Compare => mode,
1407+
// Here are the main constraints we need to deal with:
1408+
//
1409+
// 1. An opts.borrowck_mode of `BorrowckMode::Ast` is
1410+
// synonymous with no `-Z borrowck=...` flag at all.
1411+
// (This is arguably a historical accident.)
1412+
//
1413+
// 2. `BorrowckMode::Migrate` is the limited migration to
1414+
// NLL that we are deploying with the 2018 edition.
1415+
//
1416+
// 3. We want to allow developers on the Nightly channel
1417+
// to opt back into the "hard error" mode for NLL,
1418+
// (which they can do via specifying `#![feature(nll)]`
1419+
// explicitly in their crate).
1420+
//
1421+
// So, this precedence list is how pnkfelix chose to work with
1422+
// the above constraints:
1423+
//
1424+
// * `#![feature(nll)]` *always* means use NLL with hard
1425+
// errors. (To simplify the code here, it now even overrides
1426+
// a user's attempt to specify `-Z borrowck=compare`, which
1427+
// we arguably do not need anymore and should remove.)
1428+
//
1429+
// * Otherwise, if no `-Z borrowck=...` flag was given (or
1430+
// if `borrowck=ast` was specified), then use the default
1431+
// as required by the edition.
1432+
//
1433+
// * Otherwise, use the behavior requested via `-Z borrowck=...`
14031434

1404-
mode @ BorrowckMode::Ast => {
1405-
if self.features().nll {
1406-
BorrowckMode::Mir
1407-
} else {
1408-
mode
1409-
}
1410-
}
1435+
if self.features().nll { return BorrowckMode::Mir; }
14111436

1437+
match self.sess.opts.borrowck_mode {
1438+
mode @ BorrowckMode::Mir |
1439+
mode @ BorrowckMode::Compare |
1440+
mode @ BorrowckMode::Migrate => mode,
1441+
1442+
BorrowckMode::Ast => match self.sess.edition() {
1443+
Edition::Edition2015 => BorrowckMode::Ast,
1444+
Edition::Edition2018 => BorrowckMode::Migrate,
1445+
1446+
// For now, future editions mean Migrate. (But it
1447+
// would make a lot of sense for it to be changed to
1448+
// `BorrowckMode::Mir`, depending on how we plan to
1449+
// time the forcing of full migration to NLL.)
1450+
_ => BorrowckMode::Migrate,
1451+
},
14121452
}
14131453
}
14141454

src/librustc_borrowck/borrowck/check_loans.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,12 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
447447
.region_scope_tree
448448
.yield_in_scope_for_expr(scope,
449449
cmt.hir_id,
450-
self.bccx.body) {
450+
self.bccx.body)
451+
{
451452
self.bccx.cannot_borrow_across_generator_yield(borrow_span,
452453
yield_span,
453454
Origin::Ast).emit();
455+
self.bccx.signal_error();
454456
}
455457
}
456458

@@ -507,9 +509,13 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
507509
new_loan, old_loan, old_loan, new_loan).err();
508510

509511
match (err_old_new, err_new_old) {
510-
(Some(mut err), None) | (None, Some(mut err)) => err.emit(),
512+
(Some(mut err), None) | (None, Some(mut err)) => {
513+
err.emit();
514+
self.bccx.signal_error();
515+
}
511516
(Some(mut err_old), Some(mut err_new)) => {
512517
err_old.emit();
518+
self.bccx.signal_error();
513519
err_new.cancel();
514520
}
515521
(None, None) => return true,
@@ -695,6 +701,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
695701
loan_span, &self.bccx.loan_path_to_string(&loan_path),
696702
Origin::Ast)
697703
.emit();
704+
self.bccx.signal_error();
698705
}
699706
}
700707
}
@@ -745,6 +752,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
745752
};
746753

747754
err.emit();
755+
self.bccx.signal_error();
748756
}
749757
}
750758
}
@@ -914,5 +922,6 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
914922
self.bccx.cannot_assign_to_borrowed(
915923
span, loan.span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast)
916924
.emit();
925+
self.bccx.signal_error();
917926
}
918927
}

src/librustc_borrowck/borrowck/gather_loans/move_error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, errors: &Vec<Move
9999
"captured outer variable");
100100
}
101101
err.emit();
102+
bccx.signal_error();
102103
}
103104
}
104105

src/librustc_borrowck/borrowck/mod.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc::middle::dataflow::DataFlowContext;
2828
use rustc::middle::dataflow::BitwiseOperator;
2929
use rustc::middle::dataflow::DataFlowOperator;
3030
use rustc::middle::dataflow::KillFrom;
31-
use rustc::middle::borrowck::BorrowCheckResult;
31+
use rustc::middle::borrowck::{BorrowCheckResult, SignalledError};
3232
use rustc::hir::def_id::{DefId, LocalDefId};
3333
use rustc::middle::expr_use_visitor as euv;
3434
use rustc::middle::mem_categorization as mc;
@@ -42,7 +42,7 @@ use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
4242
use rustc_mir::util::suggest_ref_mut;
4343
use rustc::util::nodemap::FxHashSet;
4444

45-
use std::cell::RefCell;
45+
use std::cell::{Cell, RefCell};
4646
use std::fmt;
4747
use std::rc::Rc;
4848
use rustc_data_structures::sync::Lrc;
@@ -90,7 +90,7 @@ pub struct AnalysisData<'a, 'tcx: 'a> {
9090
fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
9191
-> Lrc<BorrowCheckResult>
9292
{
93-
assert!(tcx.use_ast_borrowck());
93+
assert!(tcx.use_ast_borrowck() || tcx.migrate_borrowck());
9494

9595
debug!("borrowck(body_owner_def_id={:?})", owner_def_id);
9696

@@ -105,6 +105,7 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
105105
// and do not need borrowchecking.
106106
return Lrc::new(BorrowCheckResult {
107107
used_mut_nodes: FxHashSet(),
108+
signalled_any_error: SignalledError::NoErrorsSeen,
108109
})
109110
}
110111
_ => { }
@@ -121,6 +122,7 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
121122
owner_def_id,
122123
body,
123124
used_mut_nodes: RefCell::new(FxHashSet()),
125+
signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
124126
};
125127

126128
// 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)
154156

155157
Lrc::new(BorrowCheckResult {
156158
used_mut_nodes: bccx.used_mut_nodes.into_inner(),
159+
signalled_any_error: bccx.signalled_any_error.into_inner(),
157160
})
158161
}
159162

@@ -234,6 +237,7 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
234237
owner_def_id,
235238
body,
236239
used_mut_nodes: RefCell::new(FxHashSet()),
240+
signalled_any_error: Cell::new(SignalledError::NoErrorsSeen),
237241
};
238242

239243
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
@@ -257,6 +261,15 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
257261
body: &'tcx hir::Body,
258262

259263
used_mut_nodes: RefCell<FxHashSet<HirId>>,
264+
265+
signalled_any_error: Cell<SignalledError>,
266+
}
267+
268+
269+
impl<'a, 'tcx: 'a> BorrowckCtxt<'a, 'tcx> {
270+
fn signal_error(&self) {
271+
self.signalled_any_error.set(SignalledError::SawSomeError);
272+
}
260273
}
261274

262275
impl<'a, 'b, 'tcx: 'b> BorrowckErrors<'a> for &'a BorrowckCtxt<'b, 'tcx> {
@@ -645,6 +658,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
645658
.span_label(use_span, format!("use of possibly uninitialized `{}`",
646659
self.loan_path_to_string(lp)))
647660
.emit();
661+
self.signal_error();
648662
return;
649663
}
650664
_ => {
@@ -760,6 +774,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
760774
// not considered particularly helpful.
761775

762776
err.emit();
777+
self.signal_error();
763778
}
764779

765780
pub fn report_partial_reinitialization_of_uninitialized_structure(
@@ -770,6 +785,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
770785
&self.loan_path_to_string(lp),
771786
Origin::Ast)
772787
.emit();
788+
self.signal_error();
773789
}
774790

775791
pub fn report_reassigned_immutable_variable(&self,
@@ -787,6 +803,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
787803
self.loan_path_to_string(lp)));
788804
}
789805
err.emit();
806+
self.signal_error();
790807
}
791808

792809
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self,
@@ -908,6 +925,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
908925
self.tcx.hir.hir_to_node_id(err.cmt.hir_id)
909926
);
910927
db.emit();
928+
self.signal_error();
911929
}
912930
err_out_of_scope(super_scope, sub_scope, cause) => {
913931
let msg = match opt_loan_path(&err.cmt) {
@@ -1022,6 +1040,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
10221040
}
10231041

10241042
db.emit();
1043+
self.signal_error();
10251044
}
10261045
err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
10271046
let descr = self.cmt_to_path_or_string(err.cmt);
@@ -1047,6 +1066,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
10471066
"");
10481067

10491068
db.emit();
1069+
self.signal_error();
10501070
}
10511071
}
10521072
}
@@ -1125,6 +1145,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
11251145
err.help("closures behind references must be called via `&mut`");
11261146
}
11271147
err.emit();
1148+
self.signal_error();
11281149
}
11291150

11301151
/// 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> {
13071328
cmt_path_or_string),
13081329
suggestion)
13091330
.emit();
1331+
self.signal_error();
13101332
}
13111333

13121334
fn region_end_span(&self, region: ty::Region<'tcx>) -> Option<Span> {

src/librustc_errors/diagnostic.rs

+19
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,25 @@ impl Diagnostic {
9999
}
100100
}
101101

102+
pub fn is_error(&self) -> bool {
103+
match self.level {
104+
Level::Bug |
105+
Level::Fatal |
106+
Level::PhaseFatal |
107+
Level::Error |
108+
Level::FailureNote => {
109+
true
110+
}
111+
112+
Level::Warning |
113+
Level::Note |
114+
Level::Help |
115+
Level::Cancelled => {
116+
false
117+
}
118+
}
119+
}
120+
102121
/// Cancel the diagnostic (a structured diagnostic must either be emitted or
103122
/// canceled or it will panic when dropped).
104123
pub fn cancel(&mut self) {

0 commit comments

Comments
 (0)