Get Polonius borrow check to work in simple cases
* Restores the generation of outlives facts from subtyping. * Restore liveness facts. * Generate invalidates facts at the start point of each location, where we check for errors. * Add a small test for simple cases.
This commit is contained in:
parent
c7df1f530b
commit
610903fb11
10 changed files with 172 additions and 80 deletions
|
|
@ -479,7 +479,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
|
|||
|
||||
/// Generate a new invalidates(L, B) fact
|
||||
fn generate_invalidates(&mut self, b: BorrowIndex, l: Location) {
|
||||
let lidx = self.location_table.mid_index(l);
|
||||
let lidx = self.location_table.start_index(l);
|
||||
self.all_facts.invalidates.push((lidx, b));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
|
||||
use borrow_check::nll::facts::AllFacts;
|
||||
use borrow_check::nll::region_infer::TypeTest;
|
||||
use borrow_check::nll::type_check::Locations;
|
||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||
|
|
@ -26,7 +24,6 @@ use syntax_pos::DUMMY_SP;
|
|||
crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
location_table: &'a LocationTable,
|
||||
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
|
@ -34,14 +31,12 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|||
category: ConstraintCategory,
|
||||
outlives_constraints: &'a mut ConstraintSet,
|
||||
type_tests: &'a mut Vec<TypeTest<'tcx>>,
|
||||
all_facts: &'a mut Option<AllFacts>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
||||
crate fn new(
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
location_table: &'a LocationTable,
|
||||
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
|
@ -49,12 +44,10 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
|||
category: ConstraintCategory,
|
||||
outlives_constraints: &'a mut ConstraintSet,
|
||||
type_tests: &'a mut Vec<TypeTest<'tcx>>,
|
||||
all_facts: &'a mut Option<AllFacts>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tcx,
|
||||
universal_regions,
|
||||
location_table,
|
||||
region_bound_pairs,
|
||||
implicit_region_bound,
|
||||
param_env,
|
||||
|
|
@ -62,7 +55,6 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
|||
category,
|
||||
outlives_constraints,
|
||||
type_tests,
|
||||
all_facts,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,23 +93,6 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
|||
let r1_vid = self.to_region_vid(r1);
|
||||
let r2_vid = self.to_region_vid(r2);
|
||||
self.add_outlives(r1_vid, r2_vid);
|
||||
|
||||
// In the new analysis, all outlives relations etc
|
||||
// "take effect" at the mid point of the statement
|
||||
// that requires them, so ignore the `at_location`.
|
||||
if let Some(all_facts) = &mut self.all_facts {
|
||||
if let Some(from_location) = self.locations.from_location() {
|
||||
all_facts.outlives.push((
|
||||
r1_vid,
|
||||
r2_vid,
|
||||
self.location_table.mid_index(from_location),
|
||||
));
|
||||
} else {
|
||||
for location in self.location_table.all_points() {
|
||||
all_facts.outlives.push((r1_vid, r2_vid, location));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnpackedKind::Type(t1) => {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::facts::AllFacts;
|
||||
use borrow_check::nll::type_check::constraint_conversion;
|
||||
use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints};
|
||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||
|
|
@ -69,19 +67,15 @@ crate struct CreateResult<'tcx> {
|
|||
crate fn create(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
universal_regions: &Rc<UniversalRegions<'tcx>>,
|
||||
constraints: &mut MirTypeckRegionConstraints<'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
) -> CreateResult<'tcx> {
|
||||
UniversalRegionRelationsBuilder {
|
||||
infcx,
|
||||
param_env,
|
||||
implicit_region_bound,
|
||||
constraints,
|
||||
location_table,
|
||||
all_facts,
|
||||
universal_regions: universal_regions.clone(),
|
||||
region_bound_pairs: Vec::new(),
|
||||
relations: UniversalRegionRelations {
|
||||
|
|
@ -210,11 +204,9 @@ impl UniversalRegionRelations<'tcx> {
|
|||
struct UniversalRegionRelationsBuilder<'this, 'gcx: 'tcx, 'tcx: 'this> {
|
||||
infcx: &'this InferCtxt<'this, 'gcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
location_table: &'this LocationTable,
|
||||
universal_regions: Rc<UniversalRegions<'tcx>>,
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
|
||||
all_facts: &'this mut Option<AllFacts>,
|
||||
|
||||
// outputs:
|
||||
relations: UniversalRegionRelations<'tcx>,
|
||||
|
|
@ -281,7 +273,6 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> {
|
|||
constraint_conversion::ConstraintConversion::new(
|
||||
self.infcx.tcx,
|
||||
&self.universal_regions,
|
||||
&self.location_table,
|
||||
&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
|
|
@ -289,7 +280,6 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> {
|
|||
ConstraintCategory::Internal,
|
||||
&mut self.constraints.outlives_constraints,
|
||||
&mut self.constraints.type_tests,
|
||||
&mut self.all_facts,
|
||||
).convert_all(&data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
//! types, instead of all variables.
|
||||
|
||||
use borrow_check::nll::ToRegionVid;
|
||||
use borrow_check::nll::facts::{AllFacts, AllFactsExt};
|
||||
use rustc::mir::{Local, Mir};
|
||||
use rustc::ty::{RegionVid, TyCtxt};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
|
@ -61,12 +62,13 @@ impl NllLivenessMap {
|
|||
mir: &Mir<'tcx>,
|
||||
) -> Self {
|
||||
let mut to_local = IndexVec::default();
|
||||
let facts_enabled = AllFacts::enabled(tcx);
|
||||
let from_local: IndexVec<Local, Option<_>> = mir.local_decls
|
||||
.iter_enumerated()
|
||||
.map(|(local, local_decl)| {
|
||||
if tcx.all_free_regions_meet(&local_decl.ty, |r| {
|
||||
free_regions.contains(&r.to_region_vid())
|
||||
}) {
|
||||
}) && !facts_enabled {
|
||||
// If all the regions in the type are free regions
|
||||
// (or there are no regions), then we don't need
|
||||
// to track liveness for this variable.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::region_infer::values::RegionValueElements;
|
||||
use borrow_check::nll::constraints::ConstraintSet;
|
||||
use borrow_check::nll::NllLivenessMap;
|
||||
|
|
@ -40,6 +41,7 @@ pub(super) fn generate<'gcx, 'tcx>(
|
|||
elements: &Rc<RegionValueElements>,
|
||||
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
) {
|
||||
debug!("liveness::generate");
|
||||
let free_regions = {
|
||||
|
|
@ -51,7 +53,7 @@ pub(super) fn generate<'gcx, 'tcx>(
|
|||
)
|
||||
};
|
||||
let liveness_map = NllLivenessMap::compute(typeck.tcx(), &free_regions, mir);
|
||||
trace::trace(typeck, mir, elements, flow_inits, move_data, &liveness_map);
|
||||
trace::trace(typeck, mir, elements, flow_inits, move_data, &liveness_map, location_table);
|
||||
}
|
||||
|
||||
/// Compute all regions that are (currently) known to outlive free
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::constraints::ConstraintCategory;
|
||||
use borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
|
||||
use borrow_check::nll::type_check::liveness::liveness_map::{LiveVar, NllLivenessMap};
|
||||
|
|
@ -49,6 +50,7 @@ pub(super) fn trace(
|
|||
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
liveness_map: &NllLivenessMap,
|
||||
location_table: &LocationTable,
|
||||
) {
|
||||
debug!("trace()");
|
||||
|
||||
|
|
@ -67,6 +69,7 @@ pub(super) fn trace(
|
|||
move_data,
|
||||
liveness_map,
|
||||
drop_data: FxHashMap::default(),
|
||||
location_table,
|
||||
};
|
||||
|
||||
LivenessResults::new(cx).compute_for_all_locals();
|
||||
|
|
@ -105,6 +108,9 @@ where
|
|||
|
||||
/// Map tracking which variables need liveness computation.
|
||||
liveness_map: &'me NllLivenessMap,
|
||||
|
||||
/// Maps between a MIR Location and a LocationIndex
|
||||
location_table: &'me LocationTable,
|
||||
}
|
||||
|
||||
struct DropData<'tcx> {
|
||||
|
|
@ -453,7 +459,13 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
|
|||
) {
|
||||
debug!("add_use_live_facts_for(value={:?})", value);
|
||||
|
||||
Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
|
||||
Self::make_all_regions_live(
|
||||
self.elements,
|
||||
&mut self.typeck,
|
||||
value,
|
||||
live_at,
|
||||
self.location_table,
|
||||
)
|
||||
}
|
||||
|
||||
/// Some variable with type `live_ty` is "drop live" at `location`
|
||||
|
|
@ -505,7 +517,13 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
|
|||
// All things in the `outlives` array may be touched by
|
||||
// the destructor and must be live at this point.
|
||||
for &kind in &drop_data.dropck_result.kinds {
|
||||
Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
|
||||
Self::make_all_regions_live(
|
||||
self.elements,
|
||||
&mut self.typeck,
|
||||
kind,
|
||||
live_at,
|
||||
self.location_table,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -514,6 +532,7 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
|
|||
typeck: &mut TypeChecker<'_, '_, 'tcx>,
|
||||
value: impl TypeFoldable<'tcx>,
|
||||
live_at: &HybridBitSet<PointIndex>,
|
||||
location_table: &LocationTable,
|
||||
) {
|
||||
debug!("make_all_regions_live(value={:?})", value);
|
||||
debug!(
|
||||
|
|
@ -532,8 +551,12 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
|
|||
.liveness_constraints
|
||||
.add_elements(live_region_vid, live_at);
|
||||
|
||||
if let Some(_) = borrowck_context.all_facts {
|
||||
bug!("polonius liveness facts not implemented yet")
|
||||
if let Some(facts) = borrowck_context.all_facts {
|
||||
for point in live_at.iter() {
|
||||
let loc = elements.to_location(point);
|
||||
facts.region_live_at.push((live_region_vid, location_table.start_index(loc)));
|
||||
facts.region_live_at.push((live_region_vid, location_table.mid_index(loc)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,13 @@ use rustc::traits::{ObligationCause, PredicateObligations};
|
|||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
|
||||
use std::fmt;
|
||||
use std::{fmt, iter};
|
||||
use std::rc::Rc;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
use transform::{MirPass, MirSource};
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use either::Either;
|
||||
|
||||
macro_rules! span_mirbug {
|
||||
($context:expr, $elem:expr, $($message:tt)*) => ({
|
||||
|
|
@ -135,37 +136,35 @@ pub(crate) fn type_check<'gcx, 'tcx>(
|
|||
} = free_region_relations::create(
|
||||
infcx,
|
||||
param_env,
|
||||
location_table,
|
||||
Some(implicit_region_bound),
|
||||
universal_regions,
|
||||
&mut constraints,
|
||||
all_facts,
|
||||
);
|
||||
|
||||
{
|
||||
let mut borrowck_context = BorrowCheckContext {
|
||||
universal_regions,
|
||||
location_table,
|
||||
borrow_set,
|
||||
all_facts,
|
||||
constraints: &mut constraints,
|
||||
};
|
||||
let mut borrowck_context = BorrowCheckContext {
|
||||
universal_regions,
|
||||
location_table,
|
||||
borrow_set,
|
||||
all_facts,
|
||||
constraints: &mut constraints,
|
||||
};
|
||||
|
||||
type_check_internal(
|
||||
infcx,
|
||||
mir_def_id,
|
||||
param_env,
|
||||
mir,
|
||||
®ion_bound_pairs,
|
||||
Some(implicit_region_bound),
|
||||
Some(&mut borrowck_context),
|
||||
Some(&universal_region_relations),
|
||||
|cx| {
|
||||
cx.equate_inputs_and_outputs(mir, universal_regions, &normalized_inputs_and_output);
|
||||
liveness::generate(cx, mir, elements, flow_inits, move_data);
|
||||
},
|
||||
);
|
||||
}
|
||||
type_check_internal(
|
||||
infcx,
|
||||
mir_def_id,
|
||||
param_env,
|
||||
mir,
|
||||
®ion_bound_pairs,
|
||||
Some(implicit_region_bound),
|
||||
Some(&mut borrowck_context),
|
||||
Some(&universal_region_relations),
|
||||
|cx| {
|
||||
cx.equate_inputs_and_outputs(mir, universal_regions, &normalized_inputs_and_output);
|
||||
liveness::generate(cx, mir, elements, flow_inits, move_data, location_table);
|
||||
|
||||
cx.borrowck_context.as_mut().map(|bcx| translate_outlives_facts(bcx));
|
||||
},
|
||||
);
|
||||
|
||||
MirTypeckResults {
|
||||
constraints,
|
||||
|
|
@ -208,6 +207,27 @@ fn type_check_internal<'a, 'gcx, 'tcx, R>(
|
|||
extra(&mut checker)
|
||||
}
|
||||
|
||||
fn translate_outlives_facts(cx: &mut BorrowCheckContext) {
|
||||
if let Some(facts) = cx.all_facts {
|
||||
let location_table = cx.location_table;
|
||||
facts.outlives.extend(
|
||||
cx.constraints.outlives_constraints.iter().flat_map(|constraint: &OutlivesConstraint| {
|
||||
if let Some(from_location) = constraint.locations.from_location() {
|
||||
Either::Left(iter::once((
|
||||
constraint.sup,
|
||||
constraint.sub,
|
||||
location_table.mid_index(from_location),
|
||||
)))
|
||||
} else {
|
||||
Either::Right(location_table.all_points().map(move |location| {
|
||||
(constraint.sup, constraint.sub, location)
|
||||
}))
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
|
||||
// We sometimes see MIR failures (notably predicate failures) due to
|
||||
// the fact that we check rvalue sized predicates here. So use `delay_span_bug`
|
||||
|
|
@ -853,7 +873,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
constraint_conversion::ConstraintConversion::new(
|
||||
self.infcx.tcx,
|
||||
borrowck_context.universal_regions,
|
||||
borrowck_context.location_table,
|
||||
self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
|
|
@ -861,7 +880,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
category,
|
||||
&mut borrowck_context.constraints.outlives_constraints,
|
||||
&mut borrowck_context.constraints.type_tests,
|
||||
&mut borrowck_context.all_facts,
|
||||
).convert_all(&data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1921,14 +1939,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
category: ConstraintCategory::Boring,
|
||||
});
|
||||
|
||||
if let Some(all_facts) = all_facts {
|
||||
all_facts.outlives.push((
|
||||
ref_region.to_region_vid(),
|
||||
borrow_region.to_region_vid(),
|
||||
location_table.mid_index(location),
|
||||
));
|
||||
}
|
||||
|
||||
match mutbl {
|
||||
hir::Mutability::MutImmutable => {
|
||||
// Immutable reference. We don't need the base
|
||||
|
|
|
|||
|
|
@ -227,8 +227,6 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> {
|
|||
locations: self.locations,
|
||||
category: self.category,
|
||||
});
|
||||
|
||||
// FIXME all facts!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
src/test/ui/nll/polonius-smoke-test.rs
Normal file
47
src/test/ui/nll/polonius-smoke-test.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Check that Polonius borrow check works for simple cases.
|
||||
// ignore-compare-mode-nll
|
||||
// compile-flags: -Z borrowck=mir -Zpolonius
|
||||
|
||||
pub fn return_ref_to_local() -> &'static i32 {
|
||||
let x = 0;
|
||||
&x //~ ERROR
|
||||
}
|
||||
|
||||
pub fn use_while_mut() {
|
||||
let mut x = 0;
|
||||
let y = &mut x;
|
||||
let z = x; //~ ERROR
|
||||
let w = y;
|
||||
}
|
||||
|
||||
pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 {
|
||||
let y = &mut *x;
|
||||
let z = x; //~ ERROR
|
||||
y
|
||||
}
|
||||
|
||||
// Cases like this are why we have Polonius.
|
||||
pub fn position_dependent_outlives(x: &mut i32, cond: bool) -> &mut i32 {
|
||||
let y = &mut *x;
|
||||
if cond {
|
||||
return y;
|
||||
} else {
|
||||
*x = 0;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
fn foo<'a, 'b>(p: &'b &'a mut usize) -> &'b usize {
|
||||
p
|
||||
}
|
||||
|
||||
// Check that we create constraints for well-formedness of function arguments
|
||||
fn well_formed_function_inputs() {
|
||||
let s = &mut 1;
|
||||
let r = &mut *s;
|
||||
let tmp = foo(&r);
|
||||
s; //~ ERROR
|
||||
tmp;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
45
src/test/ui/nll/polonius-smoke-test.stderr
Normal file
45
src/test/ui/nll/polonius-smoke-test.stderr
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
error[E0597]: `x` does not live long enough
|
||||
--> $DIR/polonius-smoke-test.rs:7:5
|
||||
|
|
||||
LL | &x //~ ERROR
|
||||
| ^^ borrowed value does not live long enough
|
||||
LL | }
|
||||
| - `x` dropped here while still borrowed
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
|
||||
error[E0503]: cannot use `x` because it was mutably borrowed
|
||||
--> $DIR/polonius-smoke-test.rs:13:13
|
||||
|
|
||||
LL | let y = &mut x;
|
||||
| ------ borrow of `x` occurs here
|
||||
LL | let z = x; //~ ERROR
|
||||
| ^ use of borrowed `x`
|
||||
LL | let w = y;
|
||||
| - borrow later used here
|
||||
|
||||
error[E0505]: cannot move out of `x` because it is borrowed
|
||||
--> $DIR/polonius-smoke-test.rs:19:13
|
||||
|
|
||||
LL | let y = &mut *x;
|
||||
| ------- borrow of `*x` occurs here
|
||||
LL | let z = x; //~ ERROR
|
||||
| ^ move out of `x` occurs here
|
||||
LL | y
|
||||
| - borrow later used here
|
||||
|
||||
error[E0505]: cannot move out of `s` because it is borrowed
|
||||
--> $DIR/polonius-smoke-test.rs:43:5
|
||||
|
|
||||
LL | let r = &mut *s;
|
||||
| ------- borrow of `*s` occurs here
|
||||
LL | let tmp = foo(&r);
|
||||
LL | s; //~ ERROR
|
||||
| ^ move out of `s` occurs here
|
||||
LL | tmp;
|
||||
| --- borrow later used here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors occurred: E0503, E0505, E0597.
|
||||
For more information about an error, try `rustc --explain E0503`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue