integrate NLL with MIR type-checker
This commit is contained in:
parent
d9e841e756
commit
4b743da596
@ -192,7 +192,7 @@ impl<'tcx> FreeRegionMap<'tcx> {
|
||||
///
|
||||
/// if `r_a` represents `'a`, this function would return `{'b, 'c}`.
|
||||
pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> {
|
||||
assert!(is_free(r_a));
|
||||
assert!(is_free(r_a) || *r_a == ty::ReStatic);
|
||||
self.relation.greater_than(&r_a)
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
|
||||
None
|
||||
} else {
|
||||
Some(nll::compute_regions(infcx, def_id, mir))
|
||||
Some(nll::compute_regions(infcx, def_id, param_env, mir))
|
||||
};
|
||||
|
||||
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind};
|
||||
use rustc::mir::{Location, Lvalue, Mir, Rvalue};
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::mir::Lvalue::Projection;
|
||||
use rustc::mir::{LvalueProjection, ProjectionElem};
|
||||
@ -21,7 +21,6 @@ use rustc::util::common::ErrorReported;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
|
||||
use super::subtype;
|
||||
use super::LivenessResults;
|
||||
use super::ToRegionVid;
|
||||
use super::region_infer::RegionInferenceContext;
|
||||
@ -179,29 +178,6 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
|
||||
self.visit_mir(self.mir);
|
||||
}
|
||||
|
||||
fn add_borrow_constraint(
|
||||
&mut self,
|
||||
location: Location,
|
||||
destination_lv: &Lvalue<'tcx>,
|
||||
borrow_region: ty::Region<'tcx>,
|
||||
_borrow_kind: BorrowKind,
|
||||
_borrowed_lv: &Lvalue<'tcx>,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let span = self.mir.source_info(location).span;
|
||||
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
|
||||
|
||||
let destination_region = match destination_ty.sty {
|
||||
ty::TyRef(r, _) => r,
|
||||
_ => bug!()
|
||||
};
|
||||
|
||||
self.regioncx.add_outlives(span,
|
||||
borrow_region.to_region_vid(),
|
||||
destination_region.to_region_vid(),
|
||||
location.successor_within_block());
|
||||
}
|
||||
|
||||
fn add_reborrow_constraint(
|
||||
&mut self,
|
||||
location: Location,
|
||||
@ -237,35 +213,22 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
|
||||
fn visit_statement(&mut self,
|
||||
block: BasicBlock,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location) {
|
||||
fn visit_rvalue(&mut self,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
location: Location) {
|
||||
debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
|
||||
|
||||
debug!("visit_statement(statement={:?}, location={:?})", statement, location);
|
||||
|
||||
// Look for a statement like:
|
||||
// Look for an rvalue like:
|
||||
//
|
||||
// D = & L
|
||||
// & L
|
||||
//
|
||||
// where D is the path to which we are assigning, and
|
||||
// L is the path that is borrowed.
|
||||
if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind {
|
||||
if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv {
|
||||
self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv);
|
||||
self.add_reborrow_constraint(location, region, borrowed_lv);
|
||||
}
|
||||
|
||||
let tcx = self.infcx.tcx;
|
||||
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
|
||||
let rv_ty = rv.ty(self.mir, tcx);
|
||||
|
||||
let span = self.mir.source_info(location).span;
|
||||
for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
|
||||
self.regioncx.add_outlives(span, a, b, location.successor_within_block());
|
||||
}
|
||||
// where L is the path that is borrowed. In that case, we have
|
||||
// to add the reborrow constraints (which don't fall out
|
||||
// naturally from the type-checker).
|
||||
if let Rvalue::Ref(region, _bk, ref borrowed_lv) = *rvalue {
|
||||
self.add_reborrow_constraint(location, region, borrowed_lv);
|
||||
}
|
||||
|
||||
self.super_statement(block, statement, location);
|
||||
self.super_rvalue(rvalue, location);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>(
|
||||
|
||||
let mut indices = FxHashMap();
|
||||
|
||||
// `'static` is always free.
|
||||
insert_free_region(&mut indices, infcx.tcx.types.re_static);
|
||||
|
||||
// Extract the early regions.
|
||||
let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
|
||||
for item_subst in item_substs {
|
||||
|
@ -15,14 +15,15 @@ use rustc::ty::{self, RegionKind, RegionVid};
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
use std::collections::BTreeSet;
|
||||
use transform::MirSource;
|
||||
use transform::type_check;
|
||||
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
|
||||
|
||||
use util as mir_util;
|
||||
use self::mir_util::PassWhere;
|
||||
|
||||
mod constraint_generation;
|
||||
mod subtype_constraint_generation;
|
||||
mod free_regions;
|
||||
mod subtype;
|
||||
|
||||
pub(crate) mod region_infer;
|
||||
use self::region_infer::RegionInferenceContext;
|
||||
@ -35,6 +36,7 @@ mod renumber;
|
||||
pub fn compute_regions<'a, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
param_env: ty::ParamEnv<'gcx>,
|
||||
mir: &mut Mir<'tcx>,
|
||||
) -> RegionInferenceContext<'tcx> {
|
||||
// Compute named region information.
|
||||
@ -43,6 +45,16 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
|
||||
// Replace all regions with fresh inference variables.
|
||||
renumber::renumber_mir(infcx, free_regions, mir);
|
||||
|
||||
// Run the MIR type-checker.
|
||||
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
|
||||
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
|
||||
|
||||
// Create the region inference context, taking ownership of the region inference
|
||||
// data that was contained in `infcx`.
|
||||
let var_origins = infcx.take_region_var_origins();
|
||||
let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
|
||||
subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
|
||||
|
||||
// Compute what is live where.
|
||||
let liveness = &LivenessResults {
|
||||
regular: liveness::liveness_of_locals(
|
||||
@ -62,12 +74,10 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
|
||||
),
|
||||
};
|
||||
|
||||
// Create the region inference context, generate the constraints,
|
||||
// and then solve them.
|
||||
let var_origins = infcx.take_region_var_origins();
|
||||
let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
|
||||
let param_env = infcx.tcx.param_env(def_id);
|
||||
// Generate non-subtyping constraints.
|
||||
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
|
||||
|
||||
// Solve the region constraints.
|
||||
regioncx.solve(infcx, &mir);
|
||||
|
||||
// Dump MIR results into a file, if that is enabled. This let us
|
||||
@ -123,12 +133,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
|
||||
match pass_where {
|
||||
// Before the CFG, dump out the values for each region variable.
|
||||
PassWhere::BeforeCFG => for region in regioncx.regions() {
|
||||
writeln!(
|
||||
out,
|
||||
"| {:?}: {:?}",
|
||||
region,
|
||||
regioncx.region_value(region)
|
||||
)?;
|
||||
writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?;
|
||||
},
|
||||
|
||||
// Before each basic block, dump out the values
|
||||
|
@ -183,6 +183,15 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
|
||||
// Add `end(X)` into the set for X.
|
||||
self.definitions[variable].value.add_free_region(variable);
|
||||
|
||||
// `'static` outlives all other free regions as well.
|
||||
if let ty::ReStatic = free_region {
|
||||
for &other_variable in indices.values() {
|
||||
self.definitions[variable]
|
||||
.value
|
||||
.add_free_region(other_variable);
|
||||
}
|
||||
}
|
||||
|
||||
// Go through each region Y that outlives X (i.e., where
|
||||
// Y: X is true). Add `end(X)` into the set for `Y`.
|
||||
for superregion in free_region_map.regions_that_outlive(&free_region) {
|
||||
|
@ -1,98 +0,0 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use transform::nll::ToRegionVid;
|
||||
use rustc::ty::{self, Ty, TyCtxt, RegionVid};
|
||||
use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
|
||||
pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>)
|
||||
-> Vec<(RegionVid, RegionVid)>
|
||||
{
|
||||
let mut subtype = Subtype::new(tcx);
|
||||
match subtype.relate(&a, &b) {
|
||||
Ok(_) => subtype.outlives_pairs,
|
||||
|
||||
Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b)
|
||||
}
|
||||
}
|
||||
|
||||
struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
outlives_pairs: Vec<(RegionVid, RegionVid)>,
|
||||
ambient_variance: ty::Variance,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> {
|
||||
Subtype {
|
||||
tcx,
|
||||
outlives_pairs: vec![],
|
||||
ambient_variance: ty::Covariant,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> {
|
||||
fn tag(&self) -> &'static str { "Subtype" }
|
||||
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx }
|
||||
fn a_is_expected(&self) -> bool { true } // irrelevant
|
||||
|
||||
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
|
||||
variance: ty::Variance,
|
||||
a: &T,
|
||||
b: &T)
|
||||
-> RelateResult<'tcx, T>
|
||||
{
|
||||
let old_ambient_variance = self.ambient_variance;
|
||||
self.ambient_variance = self.ambient_variance.xform(variance);
|
||||
|
||||
let result = self.relate(a, b);
|
||||
self.ambient_variance = old_ambient_variance;
|
||||
result
|
||||
}
|
||||
|
||||
fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
relate::super_relate_tys(self, t, t2)
|
||||
}
|
||||
|
||||
fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>)
|
||||
-> RelateResult<'tcx, ty::Region<'tcx>> {
|
||||
let a = r_a.to_region_vid();
|
||||
let b = r_b.to_region_vid();
|
||||
|
||||
match self.ambient_variance {
|
||||
ty::Covariant => {
|
||||
self.outlives_pairs.push((b, a));
|
||||
},
|
||||
|
||||
ty::Invariant => {
|
||||
self.outlives_pairs.push((a, b));
|
||||
self.outlives_pairs.push((b, a));
|
||||
},
|
||||
|
||||
ty::Contravariant => {
|
||||
self.outlives_pairs.push((a, b));
|
||||
},
|
||||
|
||||
ty::Bivariant => {},
|
||||
}
|
||||
|
||||
Ok(r_a)
|
||||
}
|
||||
|
||||
fn binders<T>(&mut self, _a: &ty::Binder<T>, _b: &ty::Binder<T>)
|
||||
-> RelateResult<'tcx, ty::Binder<T>>
|
||||
where T: Relate<'tcx>
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
112
src/librustc_mir/transform/nll/subtype_constraint_generation.rs
Normal file
112
src/librustc_mir/transform/nll/subtype_constraint_generation.rs
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::mir::Mir;
|
||||
use rustc::infer::region_constraints::Constraint;
|
||||
use rustc::infer::region_constraints::RegionConstraintData;
|
||||
use rustc::ty;
|
||||
use transform::type_check::MirTypeckRegionConstraints;
|
||||
use transform::type_check::OutlivesSet;
|
||||
|
||||
use super::free_regions::FreeRegions;
|
||||
use super::region_infer::RegionInferenceContext;
|
||||
|
||||
/// When the MIR type-checker executes, it validates all the types in
|
||||
/// the MIR, and in the process generates a set of constraints that
|
||||
/// must hold regarding the regions in the MIR, along with locations
|
||||
/// *where* they must hold. This code takes those constriants and adds
|
||||
/// them into the NLL `RegionInferenceContext`.
|
||||
pub(super) fn generate<'tcx>(
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
free_regions: &FreeRegions<'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
) {
|
||||
SubtypeConstraintGenerator {
|
||||
regioncx,
|
||||
free_regions,
|
||||
mir,
|
||||
}.generate(constraints);
|
||||
}
|
||||
|
||||
struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
|
||||
regioncx: &'cx mut RegionInferenceContext<'tcx>,
|
||||
free_regions: &'cx FreeRegions<'tcx>,
|
||||
mir: &'cx Mir<'tcx>,
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
||||
fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
|
||||
let MirTypeckRegionConstraints {
|
||||
liveness_set,
|
||||
outlives_sets,
|
||||
} = constraints;
|
||||
|
||||
debug!(
|
||||
"generate(liveness_set={} items, outlives_sets={} items)",
|
||||
liveness_set.len(),
|
||||
outlives_sets.len()
|
||||
);
|
||||
|
||||
for (region, location) in liveness_set {
|
||||
debug!("generate: {:#?} is live at {:#?}", region, location);
|
||||
let region_vid = self.to_region_vid(region);
|
||||
self.regioncx.add_live_point(region_vid, *location);
|
||||
}
|
||||
|
||||
for OutlivesSet { locations, data } in outlives_sets {
|
||||
debug!("generate: constraints at: {:#?}", locations);
|
||||
let RegionConstraintData {
|
||||
constraints,
|
||||
verifys,
|
||||
givens,
|
||||
} = data;
|
||||
|
||||
for constraint in constraints.keys() {
|
||||
debug!("generate: constraint: {:?}", constraint);
|
||||
let (a_vid, b_vid) = match constraint {
|
||||
Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid),
|
||||
Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid),
|
||||
Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)),
|
||||
Constraint::RegSubReg(a_r, b_r) => {
|
||||
(self.to_region_vid(a_r), self.to_region_vid(b_r))
|
||||
}
|
||||
};
|
||||
|
||||
// We have the constraint that `a_vid <= b_vid`. Add
|
||||
// `b_vid: a_vid` to our region checker. Note that we
|
||||
// reverse direction, because `regioncx` talks about
|
||||
// "outlives" (`>=`) whereas the region constraints
|
||||
// talk about `<=`.
|
||||
let span = self.mir.source_info(locations.from_location).span;
|
||||
self.regioncx
|
||||
.add_outlives(span, b_vid, a_vid, locations.at_location);
|
||||
}
|
||||
|
||||
assert!(verifys.is_empty(), "verifys not yet implemented");
|
||||
assert!(
|
||||
givens.is_empty(),
|
||||
"MIR type-checker does not use givens (thank goodness)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
|
||||
// Every region that we see in the constraints came from the
|
||||
// MIR or from the parameter environment. If the former, it
|
||||
// will be a region variable. If the latter, it will be in
|
||||
// the set of free regions *somewhere*.
|
||||
if let ty::ReVar(vid) = r {
|
||||
*vid
|
||||
} else {
|
||||
self.free_regions.indices[&r]
|
||||
}
|
||||
}
|
||||
}
|
53
src/test/ui/nll/get_default.rs
Normal file
53
src/test/ui/nll/get_default.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Basic test for free regions in the NLL code. This test ought to
|
||||
// report an error due to a reborrowing constraint. Right now, we get
|
||||
// a variety of errors from the older, AST-based machinery (notably
|
||||
// borrowck), and then we get the NLL error at the end.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck-mir
|
||||
|
||||
struct Map {
|
||||
}
|
||||
|
||||
impl Map {
|
||||
fn get(&self) -> Option<&String> { None }
|
||||
fn set(&mut self, v: String) { }
|
||||
}
|
||||
|
||||
fn ok(map: &mut Map) -> &String {
|
||||
loop {
|
||||
match map.get() {
|
||||
Some(v) => {
|
||||
return v;
|
||||
}
|
||||
None => {
|
||||
map.set(String::new()); // Just AST errors here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn err(map: &mut Map) -> &String {
|
||||
loop {
|
||||
match map.get() {
|
||||
Some(v) => {
|
||||
map.set(String::new()); // Both AST and MIR error here
|
||||
return v;
|
||||
}
|
||||
None => {
|
||||
map.set(String::new()); // Just AST errors here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { }
|
47
src/test/ui/nll/get_default.stderr
Normal file
47
src/test/ui/nll/get_default.stderr
Normal file
@ -0,0 +1,47 @@
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:33:17
|
||||
|
|
||||
28 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
33 | map.set(String::new()); // Just AST errors here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
37 | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:43:17
|
||||
|
|
||||
41 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
42 | Some(v) => {
|
||||
43 | map.set(String::new()); // Both AST and MIR error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
51 | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:47:17
|
||||
|
|
||||
41 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
47 | map.set(String::new()); // Just AST errors here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
51 | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `(*map)` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:43:17
|
||||
|
|
||||
41 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
42 | Some(v) => {
|
||||
43 | map.set(String::new()); // Both AST and MIR error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user