Skip to content

Commit 3ff17e7

Browse files
committed
Auto merge of #67016 - lqd:placeholder_loans, r=matthewjasper
In which we implement illegal subset relations errors using Polonius This PR is the rustc side of implementing subset errors using Polonius. That is, in ```rust fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { y } ``` returning `y` requires that `'b: 'a` but we have no evidence of that, so this is an error. (Evidence that the relation holds could come from explicit bounds, or via implied bounds). Polonius outputs one such error per CFG point where the free region's placeholder loan unexpectedly flowed into another free region. While all these CFG locations could be useful in diagnostics in the future, rustc does not do that (and the duplication is only partially handled in the rest of the errors/diagnostics infrastructure, e.g. duplicate suggestions will be shown by the "outlives suggestions" or some of the `#[rustc_*]` NLL/MIR debug dumps), so I deduplicated the errors. (The ordering also matters, otherwise some of the elided lifetime naming would change behaviour). I've blessed a couple of tests, where the output is currently suboptimal: - the `hrtb-perfect-forwarding` tests mix subset errors with higher-ranked subtyping, however the plan is for chalk to eventually take care of some of this to generate polonius constraints (i.e. it's not polonius' job). Until that happens, polonius will not see the error that NLL sees. - some other tests have errors and diagnostics specific to `'static`, I _believe_ this to be because of it being treated as more "special" than in polonius. I believe the output is not wrong, but could be better, and appears elsewhere (I feel we'll need to look at polonius' handling of `'static` at some point in the future, maybe to match a bit more what NLL does when it produces errors) I'll create a tracking issue in the polonius repo to record these 2 points (and a general "we'll need to go over the blessed output" issue, much like we did for NLLs) The last blessed test is because it's an improvement: in this case, more errors/suggestions were computed, instead of the existing code path where this case apparently stops at the first error. The `Naive` variant in Polonius computes those errors, so this PR also switches the default variant to that, as we're also in the process of temporarily deactivating all other variants (which exist mostly for performance considerations) until we have completed more work on completeness and correctness, before focusing on efficiency once again. While most of the correctness in this PR is hidden in the polonius compare-mode (which of course passes locally), I've added a couple of smoke-tests to the existing ones, so that we have some confidence that it works (and keeps working) until we're in a position where we can run them on CI. As mentioned during yesterday's wg-polonius meeting, @nikomatsakis has already read through most of this PR (and which is matching what they thought needed to be done [during the recent Polonius sprint](https://hackmd.io/CGMNjt1hR_qYtsR9hgdGmw#Compiler-notes-on-generating-the-placeholder-loans-support)), but Matthew was hopefully going to review (again, not urgent), so: r? @matthewjasper (This updates to the latest `polonius-engine` release, and I'm not sure whether `Cargo.lock` updates can easily be rolled up, but apart from that: this changes little that's tested on CI, so seems safe-ish to rollup ?)
2 parents dbbe4f1 + 1314ba3 commit 3ff17e7

File tree

16 files changed

+622
-38
lines changed

16 files changed

+622
-38
lines changed

Diff for: Cargo.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -2577,9 +2577,9 @@ checksum = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
25772577

25782578
[[package]]
25792579
name = "polonius-engine"
2580-
version = "0.10.0"
2580+
version = "0.11.0"
25812581
source = "registry+https://github.com/rust-lang/crates.io-index"
2582-
checksum = "50fa9dbfd0d3d60594da338cfe6f94028433eecae4b11b7e83fd99759227bbfe"
2582+
checksum = "1e478d7c38eb785c6416cbe58df12aa55d7aefa3759b6d3e044b2ed03f423cec"
25832583
dependencies = [
25842584
"datafrog",
25852585
"log",

Diff for: src/librustc/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ scoped-tls = "1.0"
2020
log = { version = "0.4", features = ["release_max_level_info", "std"] }
2121
rustc-rayon = "0.3.0"
2222
rustc-rayon-core = "0.3.0"
23-
polonius-engine = "0.10.0"
23+
polonius-engine = "0.11.0"
2424
rustc_apfloat = { path = "../librustc_apfloat" }
2525
rustc_feature = { path = "../librustc_feature" }
2626
rustc_target = { path = "../librustc_target" }

Diff for: src/librustc_data_structures/transitive_relation.rs

+8
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,14 @@ impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
373373
}
374374
matrix
375375
}
376+
377+
/// Lists all the base edges in the graph: the initial _non-transitive_ set of element
378+
/// relations, which will be later used as the basis for the transitive closure computation.
379+
pub fn base_edges(&self) -> impl Iterator<Item=(&T, &T)> {
380+
self.edges
381+
.iter()
382+
.map(move |edge| (&self.elements[edge.source.0], &self.elements[edge.target.0]))
383+
}
376384
}
377385

378386
/// Pare down is used as a step in the LUB computation. It edits the

Diff for: src/librustc_mir/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dot = { path = "../libgraphviz", package = "graphviz" }
1616
itertools = "0.8"
1717
log = "0.4"
1818
log_settings = "0.1.1"
19-
polonius-engine = "0.10.0"
19+
polonius-engine = "0.11.0"
2020
rustc = { path = "../librustc" }
2121
rustc_target = { path = "../librustc_target" }
2222
rustc_data_structures = { path = "../librustc_data_structures" }

Diff for: src/librustc_mir/borrow_check/flows.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
//! FIXME: this might be better as a "generic" fixed-point combinator,
44
//! but is not as ugly as it is right now.
55
6-
use rustc::mir::{BasicBlock, Local, Location};
7-
use rustc::ty::RegionVid;
6+
use rustc::mir::{BasicBlock, Location};
87
use rustc_index::bit_set::BitIter;
98

109
use crate::borrow_check::location::LocationIndex;
1110

12-
use polonius_engine::Output;
11+
use crate::borrow_check::nll::PoloniusOutput;
1312

1413
use crate::dataflow::indexes::BorrowIndex;
15-
use crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
14+
use crate::dataflow::move_paths::HasMoveData;
1615
use crate::dataflow::Borrows;
1716
use crate::dataflow::EverInitializedPlaces;
1817
use crate::dataflow::MaybeUninitializedPlaces;
@@ -21,8 +20,6 @@ use either::Either;
2120
use std::fmt;
2221
use std::rc::Rc;
2322

24-
crate type PoloniusOutput = Output<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>;
25-
2623
crate struct Flows<'b, 'tcx> {
2724
borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>,
2825
pub uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'tcx>>,

Diff for: src/librustc_mir/borrow_check/nll/facts.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::borrow_check::location::{LocationIndex, LocationTable};
22
use crate::dataflow::indexes::{BorrowIndex, MovePathIndex};
3-
use polonius_engine::AllFacts as PoloniusAllFacts;
3+
use polonius_engine::AllFacts as PoloniusFacts;
44
use polonius_engine::Atom;
55
use rustc::mir::Local;
66
use rustc::ty::{RegionVid, TyCtxt};
@@ -11,7 +11,18 @@ use std::fs::{self, File};
1111
use std::io::Write;
1212
use std::path::Path;
1313

14-
crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>;
14+
#[derive(Copy, Clone, Debug)]
15+
crate struct RustcFacts;
16+
17+
impl polonius_engine::FactTypes for RustcFacts {
18+
type Origin = RegionVid;
19+
type Loan = BorrowIndex;
20+
type Point = LocationIndex;
21+
type Variable = Local;
22+
type Path = MovePathIndex;
23+
}
24+
25+
crate type AllFacts = PoloniusFacts<RustcFacts>;
1526

1627
crate trait AllFactsExt {
1728
/// Returns `true` if there is a need to gather `AllFacts` given the
@@ -55,6 +66,7 @@ impl AllFactsExt for AllFacts {
5566
wr.write_facts_to_path(self.[
5667
borrow_region,
5768
universal_region,
69+
placeholder,
5870
cfg_edge,
5971
killed,
6072
outlives,
@@ -69,6 +81,7 @@ impl AllFactsExt for AllFacts {
6981
initialized_at,
7082
moved_out_at,
7183
path_accessed_at,
84+
known_subset,
7285
])
7386
}
7487
Ok(())

Diff for: src/librustc_mir/borrow_check/nll/mod.rs

+49-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use crate::borrow_check::borrow_set::BorrowSet;
2-
use crate::borrow_check::location::{LocationIndex, LocationTable};
2+
use crate::borrow_check::location::LocationTable;
33
use crate::borrow_check::nll::facts::AllFactsExt;
44
use crate::borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints};
55
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
6-
use crate::dataflow::indexes::BorrowIndex;
7-
use crate::dataflow::move_paths::{InitLocation, MoveData, MovePathIndex, InitKind};
6+
use crate::dataflow::move_paths::{InitLocation, MoveData, InitKind};
87
use crate::dataflow::FlowAtLocation;
98
use crate::dataflow::MaybeInitializedPlaces;
109
use crate::transform::MirSource;
@@ -43,10 +42,12 @@ crate mod universal_regions;
4342
crate mod type_check;
4443
crate mod region_infer;
4544

46-
use self::facts::AllFacts;
45+
use self::facts::{AllFacts, RustcFacts};
4746
use self::region_infer::RegionInferenceContext;
4847
use self::universal_regions::UniversalRegions;
4948

49+
crate type PoloniusOutput = Output<RustcFacts>;
50+
5051
/// Rewrites the regions in the MIR to use NLL variables, also
5152
/// scraping out the set of universal regions (e.g., region parameters)
5253
/// declared on the function. That set will need to be given to
@@ -170,7 +171,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
170171
errors_buffer: &mut Vec<Diagnostic>,
171172
) -> (
172173
RegionInferenceContext<'tcx>,
173-
Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>>>,
174+
Option<Rc<PoloniusOutput>>,
174175
Option<ClosureRegionRequirements<'tcx>>,
175176
) {
176177
let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default());
@@ -204,6 +205,39 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
204205
.universal_region
205206
.extend(universal_regions.universal_regions());
206207
populate_polonius_move_facts(all_facts, move_data, location_table, &body);
208+
209+
// Emit universal regions facts, and their relations, for Polonius.
210+
//
211+
// 1: universal regions are modeled in Polonius as a pair:
212+
// - the universal region vid itself.
213+
// - a "placeholder loan" associated to this universal region. Since they don't exist in
214+
// the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
215+
// added to the existing number of loans, as if they succeeded them in the set.
216+
//
217+
let borrow_count = borrow_set.borrows.len();
218+
debug!(
219+
"compute_regions: polonius placeholders, num_universals={}, borrow_count={}",
220+
universal_regions.len(),
221+
borrow_count
222+
);
223+
224+
for universal_region in universal_regions.universal_regions() {
225+
let universal_region_idx = universal_region.index();
226+
let placeholder_loan_idx = borrow_count + universal_region_idx;
227+
all_facts.placeholder.push((universal_region, placeholder_loan_idx.into()));
228+
}
229+
230+
// 2: the universal region relations `outlives` constraints are emitted as
231+
// `known_subset` facts.
232+
for (fr1, fr2) in universal_region_relations.known_outlives() {
233+
if fr1 != fr2 {
234+
debug!(
235+
"compute_regions: emitting polonius `known_subset` fr1={:?}, fr2={:?}",
236+
fr1, fr2
237+
);
238+
all_facts.known_subset.push((*fr1, *fr2));
239+
}
240+
}
207241
}
208242

209243
// Create the region inference context, taking ownership of the
@@ -265,7 +299,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
265299

266300
if infcx.tcx.sess.opts.debugging_opts.polonius {
267301
let algorithm = env::var("POLONIUS_ALGORITHM")
268-
.unwrap_or_else(|_| String::from("Hybrid"));
302+
.unwrap_or_else(|_| String::from("Naive"));
269303
let algorithm = Algorithm::from_str(&algorithm).unwrap();
270304
debug!("compute_regions: using polonius algorithm {:?}", algorithm);
271305
Some(Rc::new(Output::compute(
@@ -279,8 +313,15 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
279313
});
280314

281315
// Solve the region constraints.
282-
let closure_region_requirements =
283-
regioncx.solve(infcx, &body, local_names, upvars, def_id, errors_buffer);
316+
let closure_region_requirements = regioncx.solve(
317+
infcx,
318+
&body,
319+
local_names,
320+
upvars,
321+
def_id,
322+
errors_buffer,
323+
polonius_output.clone(),
324+
);
284325

285326
// Dump MIR results into a file, if that is enabled. This let us
286327
// write unit-tests, as well as helping with debugging.

0 commit comments

Comments
 (0)