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 ?)
This commit is contained in:
bors 2019-12-09 10:50:41 +00:00
commit 3ff17e7c5f
16 changed files with 622 additions and 38 deletions

View File

@ -2577,9 +2577,9 @@ checksum = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
[[package]]
name = "polonius-engine"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50fa9dbfd0d3d60594da338cfe6f94028433eecae4b11b7e83fd99759227bbfe"
checksum = "1e478d7c38eb785c6416cbe58df12aa55d7aefa3759b6d3e044b2ed03f423cec"
dependencies = [
"datafrog",
"log",

View File

@ -20,7 +20,7 @@ scoped-tls = "1.0"
log = { version = "0.4", features = ["release_max_level_info", "std"] }
rustc-rayon = "0.3.0"
rustc-rayon-core = "0.3.0"
polonius-engine = "0.10.0"
polonius-engine = "0.11.0"
rustc_apfloat = { path = "../librustc_apfloat" }
rustc_feature = { path = "../librustc_feature" }
rustc_target = { path = "../librustc_target" }

View File

@ -373,6 +373,14 @@ impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
}
matrix
}
/// Lists all the base edges in the graph: the initial _non-transitive_ set of element
/// relations, which will be later used as the basis for the transitive closure computation.
pub fn base_edges(&self) -> impl Iterator<Item=(&T, &T)> {
self.edges
.iter()
.map(move |edge| (&self.elements[edge.source.0], &self.elements[edge.target.0]))
}
}
/// Pare down is used as a step in the LUB computation. It edits the

View File

@ -16,7 +16,7 @@ dot = { path = "../libgraphviz", package = "graphviz" }
itertools = "0.8"
log = "0.4"
log_settings = "0.1.1"
polonius-engine = "0.10.0"
polonius-engine = "0.11.0"
rustc = { path = "../librustc" }
rustc_target = { path = "../librustc_target" }
rustc_data_structures = { path = "../librustc_data_structures" }

View File

@ -3,16 +3,15 @@
//! FIXME: this might be better as a "generic" fixed-point combinator,
//! but is not as ugly as it is right now.
use rustc::mir::{BasicBlock, Local, Location};
use rustc::ty::RegionVid;
use rustc::mir::{BasicBlock, Location};
use rustc_index::bit_set::BitIter;
use crate::borrow_check::location::LocationIndex;
use polonius_engine::Output;
use crate::borrow_check::nll::PoloniusOutput;
use crate::dataflow::indexes::BorrowIndex;
use crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
use crate::dataflow::move_paths::HasMoveData;
use crate::dataflow::Borrows;
use crate::dataflow::EverInitializedPlaces;
use crate::dataflow::MaybeUninitializedPlaces;
@ -21,8 +20,6 @@ use either::Either;
use std::fmt;
use std::rc::Rc;
crate type PoloniusOutput = Output<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>;
crate struct Flows<'b, 'tcx> {
borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>,
pub uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'tcx>>,

View File

@ -1,6 +1,6 @@
use crate::borrow_check::location::{LocationIndex, LocationTable};
use crate::dataflow::indexes::{BorrowIndex, MovePathIndex};
use polonius_engine::AllFacts as PoloniusAllFacts;
use polonius_engine::AllFacts as PoloniusFacts;
use polonius_engine::Atom;
use rustc::mir::Local;
use rustc::ty::{RegionVid, TyCtxt};
@ -11,7 +11,18 @@ use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>;
#[derive(Copy, Clone, Debug)]
crate struct RustcFacts;
impl polonius_engine::FactTypes for RustcFacts {
type Origin = RegionVid;
type Loan = BorrowIndex;
type Point = LocationIndex;
type Variable = Local;
type Path = MovePathIndex;
}
crate type AllFacts = PoloniusFacts<RustcFacts>;
crate trait AllFactsExt {
/// Returns `true` if there is a need to gather `AllFacts` given the
@ -55,6 +66,7 @@ impl AllFactsExt for AllFacts {
wr.write_facts_to_path(self.[
borrow_region,
universal_region,
placeholder,
cfg_edge,
killed,
outlives,
@ -69,6 +81,7 @@ impl AllFactsExt for AllFacts {
initialized_at,
moved_out_at,
path_accessed_at,
known_subset,
])
}
Ok(())

View File

@ -1,10 +1,9 @@
use crate::borrow_check::borrow_set::BorrowSet;
use crate::borrow_check::location::{LocationIndex, LocationTable};
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::facts::AllFactsExt;
use crate::borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints};
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
use crate::dataflow::indexes::BorrowIndex;
use crate::dataflow::move_paths::{InitLocation, MoveData, MovePathIndex, InitKind};
use crate::dataflow::move_paths::{InitLocation, MoveData, InitKind};
use crate::dataflow::FlowAtLocation;
use crate::dataflow::MaybeInitializedPlaces;
use crate::transform::MirSource;
@ -43,10 +42,12 @@ crate mod universal_regions;
crate mod type_check;
crate mod region_infer;
use self::facts::AllFacts;
use self::facts::{AllFacts, RustcFacts};
use self::region_infer::RegionInferenceContext;
use self::universal_regions::UniversalRegions;
crate type PoloniusOutput = Output<RustcFacts>;
/// Rewrites the regions in the MIR to use NLL variables, also
/// scraping out the set of universal regions (e.g., region parameters)
/// 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>(
errors_buffer: &mut Vec<Diagnostic>,
) -> (
RegionInferenceContext<'tcx>,
Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex, Local, MovePathIndex>>>,
Option<Rc<PoloniusOutput>>,
Option<ClosureRegionRequirements<'tcx>>,
) {
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>(
.universal_region
.extend(universal_regions.universal_regions());
populate_polonius_move_facts(all_facts, move_data, location_table, &body);
// Emit universal regions facts, and their relations, for Polonius.
//
// 1: universal regions are modeled in Polonius as a pair:
// - the universal region vid itself.
// - a "placeholder loan" associated to this universal region. Since they don't exist in
// the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
// added to the existing number of loans, as if they succeeded them in the set.
//
let borrow_count = borrow_set.borrows.len();
debug!(
"compute_regions: polonius placeholders, num_universals={}, borrow_count={}",
universal_regions.len(),
borrow_count
);
for universal_region in universal_regions.universal_regions() {
let universal_region_idx = universal_region.index();
let placeholder_loan_idx = borrow_count + universal_region_idx;
all_facts.placeholder.push((universal_region, placeholder_loan_idx.into()));
}
// 2: the universal region relations `outlives` constraints are emitted as
// `known_subset` facts.
for (fr1, fr2) in universal_region_relations.known_outlives() {
if fr1 != fr2 {
debug!(
"compute_regions: emitting polonius `known_subset` fr1={:?}, fr2={:?}",
fr1, fr2
);
all_facts.known_subset.push((*fr1, *fr2));
}
}
}
// Create the region inference context, taking ownership of the
@ -265,7 +299,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
if infcx.tcx.sess.opts.debugging_opts.polonius {
let algorithm = env::var("POLONIUS_ALGORITHM")
.unwrap_or_else(|_| String::from("Hybrid"));
.unwrap_or_else(|_| String::from("Naive"));
let algorithm = Algorithm::from_str(&algorithm).unwrap();
debug!("compute_regions: using polonius algorithm {:?}", algorithm);
Some(Rc::new(Output::compute(
@ -279,8 +313,15 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
});
// Solve the region constraints.
let closure_region_requirements =
regioncx.solve(infcx, &body, local_names, upvars, def_id, errors_buffer);
let closure_region_requirements = regioncx.solve(
infcx,
&body,
local_names,
upvars,
def_id,
errors_buffer,
polonius_output.clone(),
);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging.

View File

@ -44,7 +44,7 @@ use crate::borrow_check::{
use self::values::{LivenessValues, RegionValueElements, RegionValues};
use super::universal_regions::UniversalRegions;
use super::ToRegionVid;
use super::{PoloniusOutput, ToRegionVid};
mod dump_mir;
mod graphviz;
@ -484,6 +484,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
upvars: &[Upvar],
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
polonius_output: Option<Rc<PoloniusOutput>>,
) -> Option<ClosureRegionRequirements<'tcx>> {
self.propagate_constraints(body);
@ -509,16 +510,33 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// multiple problems.
let mut region_naming = RegionErrorNamingCtx::new();
self.check_universal_regions(
infcx,
body,
local_names,
upvars,
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
&mut region_naming,
);
// In Polonius mode, the errors about missing universal region relations are in the output
// and need to be emitted or propagated. Otherwise, we need to check whether the
// constraints were too strong, and if so, emit or propagate those errors.
if infcx.tcx.sess.opts.debugging_opts.polonius {
self.check_polonius_subset_errors(
infcx,
body,
local_names,
upvars,
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
&mut region_naming,
polonius_output.expect("Polonius output is unavailable despite `-Z polonius`"),
);
} else {
self.check_universal_regions(
infcx,
body,
local_names,
upvars,
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
&mut region_naming,
);
}
self.check_member_constraints(infcx, mir_def_id, errors_buffer);
@ -1372,6 +1390,114 @@ impl<'tcx> RegionInferenceContext<'tcx> {
outlives_suggestion.add_suggestion(body, self, infcx, errors_buffer, region_naming);
}
/// Checks if Polonius has found any unexpected free region relations.
///
/// In Polonius terms, a "subset error" (or "illegal subset relation error") is the equivalent
/// of NLL's "checking if any region constraints were too strong": a placeholder origin `'a`
/// was unexpectedly found to be a subset of another placeholder origin `'b`, and means in NLL
/// terms that the "longer free region" `'a` outlived the "shorter free region" `'b`.
///
/// More details can be found in this blog post by Niko:
/// http://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/
///
/// In the canonical example
///
/// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
///
/// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a
/// constraint that `'a: 'b`. It is an error that we have no evidence that this
/// constraint holds.
///
/// If `propagated_outlives_requirements` is `Some`, then we will
/// push unsatisfied obligations into there. Otherwise, we'll
/// report them as errors.
fn check_polonius_subset_errors(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
polonius_output: Rc<PoloniusOutput>,
) {
debug!(
"check_polonius_subset_errors: {} subset_errors",
polonius_output.subset_errors.len()
);
let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names);
// Similarly to `check_universal_regions`: a free region relation, which was not explicitly
// declared ("known") was found by Polonius, so emit an error, or propagate the
// requirements for our caller into the `propagated_outlives_requirements` vector.
//
// Polonius doesn't model regions ("origins") as CFG-subsets or durations, but the
// `longer_fr` and `shorter_fr` terminology will still be used here, for consistency with
// the rest of the NLL infrastructure. The "subset origin" is the "longer free region",
// and the "superset origin" is the outlived "shorter free region".
//
// Note: Polonius will produce a subset error at every point where the unexpected
// `longer_fr`'s "placeholder loan" is contained in the `shorter_fr`. This can be helpful
// for diagnostics in the future, e.g. to point more precisely at the key locations
// requiring this constraint to hold. However, the error and diagnostics code downstream
// expects that these errors are not duplicated (and that they are in a certain order).
// Otherwise, diagnostics messages such as the ones giving names like `'1` to elided or
// anonymous lifetimes for example, could give these names differently, while others like
// the outlives suggestions or the debug output from `#[rustc_regions]` would be
// duplicated. The polonius subset errors are deduplicated here, while keeping the
// CFG-location ordering.
let mut subset_errors: Vec<_> = polonius_output
.subset_errors
.iter()
.flat_map(|(_location, subset_errors)| subset_errors.iter())
.collect();
subset_errors.sort();
subset_errors.dedup();
for (longer_fr, shorter_fr) in subset_errors.into_iter() {
debug!("check_polonius_subset_errors: subset_error longer_fr={:?},\
shorter_fr={:?}", longer_fr, shorter_fr);
self.report_or_propagate_universal_region_error(
*longer_fr,
*shorter_fr,
infcx,
body,
local_names,
upvars,
mir_def_id,
&mut propagated_outlives_requirements,
&mut outlives_suggestion,
errors_buffer,
region_naming,
);
}
// Handle the placeholder errors as usual, until the chalk-rustc-polonius triumvirate has
// a more complete picture on how to separate this responsibility.
for (fr, fr_definition) in self.definitions.iter_enumerated() {
match fr_definition.origin {
NLLRegionVariableOrigin::FreeRegion => {
// handled by polonius above
}
NLLRegionVariableOrigin::Placeholder(placeholder) => {
self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder);
}
NLLRegionVariableOrigin::Existential { .. } => {
// nothing to check here
}
}
}
// Emit outlives suggestions
outlives_suggestion.add_suggestion(body, self, infcx, errors_buffer, region_naming);
}
/// Checks the final value for the free region `fr` to see if it
/// grew too large. In particular, examine what `end(X)` points
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
@ -1471,8 +1597,37 @@ impl<'tcx> RegionInferenceContext<'tcx> {
return None;
}
self.report_or_propagate_universal_region_error(
longer_fr,
shorter_fr,
infcx,
body,
local_names,
upvars,
mir_def_id,
propagated_outlives_requirements,
outlives_suggestion,
errors_buffer,
region_naming,
)
}
fn report_or_propagate_universal_region_error(
&self,
longer_fr: RegionVid,
shorter_fr: RegionVid,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) -> Option<ErrorReported> {
debug!(
"check_universal_region_relation: fr={:?} does not outlive shorter_fr={:?}",
"report_or_propagate_universal_region_error: fr={:?} does not outlive shorter_fr={:?}",
longer_fr, shorter_fr,
);
@ -1481,9 +1636,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// We'll call it `fr-` -- it's ever so slightly smaller than
// `longer_fr`.
if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
{
debug!("check_universal_region: fr_minus={:?}", fr_minus);
if let Some(fr_minus) =
self.universal_region_relations.non_local_lower_bound(longer_fr) {
debug!("report_or_propagate_universal_region_error: fr_minus={:?}", fr_minus);
let blame_span_category =
self.find_outlives_blame_span(body, longer_fr,
@ -1492,9 +1647,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Grow `shorter_fr` until we find some non-local regions. (We
// always will.) We'll call them `shorter_fr+` -- they're ever
// so slightly larger than `shorter_fr`.
let shorter_fr_plus =
self.universal_region_relations.non_local_upper_bounds(&shorter_fr);
debug!("check_universal_region: shorter_fr_plus={:?}", shorter_fr_plus);
let shorter_fr_plus = self
.universal_region_relations
.non_local_upper_bounds(&shorter_fr);
debug!(
"report_or_propagate_universal_region_error: shorter_fr_plus={:?}",
shorter_fr_plus
);
for &&fr in &shorter_fr_plus {
// Push the constraint `fr-: shorter_fr+`
propagated_outlives_requirements.push(ClosureOutlivesRequirement {

View File

@ -217,6 +217,11 @@ impl UniversalRegionRelations<'tcx> {
crate fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
self.outlives.reachable_from(&fr1)
}
/// Returns the _non-transitive_ set of known `outlives` constraints between free regions.
crate fn known_outlives(&self) -> impl Iterator<Item=(&RegionVid, &RegionVid)> {
self.outlives.base_edges()
}
}
struct UniversalRegionRelationsBuilder<'this, 'tcx> {

View File

@ -0,0 +1,56 @@
error[E0521]: borrowed data escapes outside of closure
--> $DIR/expect-region-supply-region.rs:18:9
|
LL | let mut f: Option<&u32> = None;
| ----- `f` is declared here, outside of the closure body
LL | closure_expecting_bound(|x| {
| - `x` is a reference that is only valid in the closure body
LL | f = Some(x);
| ^^^^^^^^^^^ `x` escapes the closure body here
error[E0521]: borrowed data escapes outside of closure
--> $DIR/expect-region-supply-region.rs:28:9
|
LL | let mut f: Option<&u32> = None;
| ----- `f` is declared here, outside of the closure body
LL | closure_expecting_bound(|x: &u32| {
| - `x` is a reference that is only valid in the closure body
LL | f = Some(x);
| ^^^^^^^^^^^ `x` escapes the closure body here
error: lifetime may not live long enough
--> $DIR/expect-region-supply-region.rs:37:30
|
LL | fn expect_bound_supply_named<'x>() {
| -- lifetime `'x` defined here
...
LL | closure_expecting_bound(|x: &'x u32| {
| ^ - let's call the lifetime of this reference `'1`
| |
| requires that `'1` must outlive `'x`
error[E0521]: borrowed data escapes outside of closure
--> $DIR/expect-region-supply-region.rs:42:9
|
LL | let mut f: Option<&u32> = None;
| ----- `f` is declared here, outside of the closure body
...
LL | closure_expecting_bound(|x: &'x u32| {
| - `x` is a reference that is only valid in the closure body
...
LL | f = Some(x);
| ^^^^^^^^^^^ `x` escapes the closure body here
error: lifetime may not live long enough
--> $DIR/expect-region-supply-region.rs:37:30
|
LL | fn expect_bound_supply_named<'x>() {
| -- lifetime `'x` defined here
...
LL | closure_expecting_bound(|x: &'x u32| {
| ^ requires that `'x` must outlive `'static`
|
= help: consider replacing `'x` with `'static`
error: aborting due to 5 previous errors

View File

@ -0,0 +1,68 @@
warning: function cannot return without recursing
--> $DIR/hrtb-perfect-forwarding.rs:22:1
|
LL | / fn no_hrtb<'b,T>(mut t: T)
LL | | where T : Bar<&'b isize>
LL | | {
LL | | // OK -- `T : Bar<&'b isize>`, and thus the impl above ensures that
LL | | // `&mut T : Bar<&'b isize>`.
LL | | no_hrtb(&mut t);
| | --------------- recursive call site
LL | | }
| |_^ cannot return without recursing
|
= note: `#[warn(unconditional_recursion)]` on by default
= help: a `loop` may express intention better if this is on purpose
warning: function cannot return without recursing
--> $DIR/hrtb-perfect-forwarding.rs:30:1
|
LL | / fn bar_hrtb<T>(mut t: T)
LL | | where T : for<'b> Bar<&'b isize>
LL | | {
LL | | // OK -- `T : for<'b> Bar<&'b isize>`, and thus the impl above
... |
LL | | bar_hrtb(&mut t);
| | ---------------- recursive call site
LL | | }
| |_^ cannot return without recursing
|
= help: a `loop` may express intention better if this is on purpose
warning: function cannot return without recursing
--> $DIR/hrtb-perfect-forwarding.rs:39:1
|
LL | / fn foo_hrtb_bar_not<'b,T>(mut t: T)
LL | | where T : for<'a> Foo<&'a isize> + Bar<&'b isize>
LL | | {
LL | | // Not OK -- The forwarding impl for `Foo` requires that `Bar` also
... |
LL | | foo_hrtb_bar_not(&mut t);
| | ------------------------ recursive call site
LL | | }
| |_^ cannot return without recursing
|
= help: a `loop` may express intention better if this is on purpose
error: higher-ranked subtype error
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: function cannot return without recursing
--> $DIR/hrtb-perfect-forwarding.rs:49:1
|
LL | / fn foo_hrtb_bar_hrtb<T>(mut t: T)
LL | | where T : for<'a> Foo<&'a isize> + for<'b> Bar<&'b isize>
LL | | {
LL | | // OK -- now we have `T : for<'b> Bar&'b isize>`.
LL | | foo_hrtb_bar_hrtb(&mut t);
| | ------------------------- recursive call site
LL | | }
| |_^ cannot return without recursing
|
= help: a `loop` may express intention better if this is on purpose
error: aborting due to previous error

View File

@ -0,0 +1,12 @@
error: lifetime may not live long enough
--> $DIR/error-handling.rs:13:56
|
LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> {
| -- -- lifetime `'b` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'b`
| |
| lifetime `'a` defined here
|
= help: consider adding the following bound: `'a: 'b`
error: aborting due to previous error

View File

@ -0,0 +1,121 @@
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:6:5
|
LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | x
| ^ returning this value requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:10:5
|
LL | fn foo2<'a>(x: &'a usize) -> &'static usize {
| -- lifetime `'a` defined here
LL | x
| ^ returning this value requires that `'a` must outlive `'static`
|
= help: consider replacing `'a` with `'static`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:14:5
|
LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | (x, y)
| ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:14:5
|
LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | (x, y)
| ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
help: `'a` and `'b` must be the same: replace one with the other
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:22:5
|
LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | (x, x)
| ^^^^^^ returning this value requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:22:5
|
LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) {
| -- -- lifetime `'c` defined here
| |
| lifetime `'a` defined here
...
LL | (x, x)
| ^^^^^^ returning this value requires that `'a` must outlive `'c`
|
= help: consider adding the following bound: `'a: 'c`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:31:9
|
LL | pub fn foo<'a>(x: &'a usize) -> Self {
| -- lifetime `'a` defined here
LL | Foo { x }
| ^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
= help: consider replacing `'a` with `'static`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:41:9
|
LL | impl<'a> Bar<'a> {
| -- lifetime `'a` defined here
LL | pub fn get<'b>(&self) -> &'b usize {
| -- lifetime `'b` defined here
LL | self.x
| ^^^^^^ returning this value requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:52:9
|
LL | impl<'a> Baz<'a> {
| -- lifetime `'a` defined here
LL | fn get<'b>(&'b self) -> &'a i32 {
| -- lifetime `'b` defined here
LL | self.x
| ^^^^^^ returning this value requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`
error[E0521]: borrowed data escapes outside of function
--> $DIR/outlives-suggestion-simple.rs:73:9
|
LL | fn get_bar(&self) -> Bar2 {
| -----
| |
| `self` is declared here, outside of the function body
| `self` is a reference that is only valid in the function body
LL | Bar2::new(&self)
| ^^^^^^^^^^^^^^^^ `self` escapes the function body here
error: aborting due to 10 previous errors

View File

@ -0,0 +1,30 @@
// Checks that Polonius can compute cases of universal regions errors:
// "illegal subset relation errors", cases where analysis finds that
// two free regions outlive each other, without any evidence that this
// relation holds.
// ignore-compare-mode-nll
// compile-flags: -Z borrowck=mir -Zpolonius
// returning `y` requires that `'b: 'a`, but it's not known to be true
fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
y //~ ERROR
}
// `'b: 'a` is explicitly declared
fn valid_subset<'a, 'b: 'a>(x: &'a u32, y: &'b u32) -> &'a u32 {
y
}
// because of `x`, it is implied that `'b: 'a` holds
fn implied_bounds_subset<'a, 'b>(x: &'a &'b mut u32) -> &'a u32 {
x
}
// `'b: 'a` is declared, and `'a: 'c` is known via implied bounds:
// `'b: 'c` is therefore known to hold transitively
fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 {
y
}
fn main() {}

View File

@ -0,0 +1,14 @@
error: lifetime may not live long enough
--> $DIR/subset-relations.rs:11:5
|
LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | y
| ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
error: aborting due to previous error

View File

@ -0,0 +1,60 @@
error: lifetime may not live long enough
--> $DIR/closure-substs.rs:8:16
|
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
...
LL | return x;
| ^ returning this value requires that `'a` must outlive `'static`
|
= help: consider replacing `'a` with `'static`
error: lifetime may not live long enough
--> $DIR/closure-substs.rs:15:16
|
LL | |x: &i32| -> &'static i32 {
| - let's call the lifetime of this reference `'1`
LL | return x;
| ^ returning this value requires that `'1` must outlive `'static`
error: lifetime may not live long enough
--> $DIR/closure-substs.rs:15:16
|
LL | |x: &i32| -> &'static i32 {
| - ------------ return type of closure is &'2 i32
| |
| let's call the lifetime of this reference `'1`
LL | return x;
| ^ returning this value requires that `'1` must outlive `'2`
error: lifetime may not live long enough
--> $DIR/closure-substs.rs:22:9
|
LL | fn bar<'a>() {
| -- lifetime `'a` defined here
...
LL | b(x);
| ^^^^ argument requires that `'a` must outlive `'static`
|
= help: consider replacing `'a` with `'static`
error[E0521]: borrowed data escapes outside of closure
--> $DIR/closure-substs.rs:29:9
|
LL | |x: &i32, b: fn(&'static i32)| {
| - `x` is a reference that is only valid in the closure body
LL | b(x);
| ^^^^ `x` escapes the closure body here
error[E0521]: borrowed data escapes outside of closure
--> $DIR/closure-substs.rs:29:9
|
LL | |x: &i32, b: fn(&'static i32)| {
| - - `b` is declared here, outside of the closure body
| |
| `x` is a reference that is only valid in the closure body
LL | b(x);
| ^^^^ `x` escapes the closure body here
error: aborting due to 6 previous errors