Skip to content

In which we implement illegal subset relations errors using Polonius #67016

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 9, 2019
Merged
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -2575,9 +2575,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",
2 changes: 1 addition & 1 deletion src/librustc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
8 changes: 8 additions & 0 deletions src/librustc_data_structures/transitive_relation.rs
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/librustc_mir/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
9 changes: 3 additions & 6 deletions src/librustc_mir/borrow_check/flows.rs
Original file line number Diff line number Diff line change
@@ -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>>,
17 changes: 15 additions & 2 deletions src/librustc_mir/borrow_check/nll/facts.rs
Original file line number Diff line number Diff line change
@@ -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(())
57 changes: 49 additions & 8 deletions src/librustc_mir/borrow_check/nll/mod.rs
Original file line number Diff line number Diff line change
@@ -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 = if AllFacts::enabled(infcx.tcx) {
@@ -208,6 +209,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
@@ -269,7 +303,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(
@@ -283,8 +317,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.
195 changes: 177 additions & 18 deletions src/librustc_mir/borrow_check/nll/region_infer/mod.rs
Original file line number Diff line number Diff line change
@@ -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);

@@ -1375,6 +1393,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 !=
@@ -1474,8 +1600,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,
);

@@ -1484,9 +1639,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,
@@ -1495,9 +1650,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 {
Original file line number Diff line number Diff line change
@@ -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> {
Original file line number Diff line number Diff line change
@@ -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

68 changes: 68 additions & 0 deletions src/test/ui/hrtb/hrtb-perfect-forwarding.polonius.stderr
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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

121 changes: 121 additions & 0 deletions src/test/ui/nll/outlives-suggestion-simple.polonius.stderr
Original file line number Diff line number Diff line change
@@ -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

30 changes: 30 additions & 0 deletions src/test/ui/nll/polonius/subset-relations.rs
Original file line number Diff line number Diff line change
@@ -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() {}
14 changes: 14 additions & 0 deletions src/test/ui/nll/polonius/subset-relations.stderr
Original file line number Diff line number Diff line change
@@ -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

60 changes: 60 additions & 0 deletions src/test/ui/nll/user-annotations/closure-substs.polonius.stderr
Original file line number Diff line number Diff line change
@@ -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