From 434d59a2c960dee2a17ae2625c8844986d580d7e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 7 May 2018 20:02:19 -0400 Subject: [PATCH] ignore the point where the outlives requirement was added --- src/librustc_data_structures/bitvec.rs | 2 +- .../borrow_check/nll/region_infer/dfs.rs | 265 ------------------ .../borrow_check/nll/region_infer/mod.rs | 134 ++++----- .../borrow_check/nll/region_infer/values.rs | 70 ++--- src/test/ui/nll/get_default.rs | 6 +- src/test/ui/nll/get_default.stderr | 54 +++- 6 files changed, 122 insertions(+), 409 deletions(-) delete mode 100644 src/librustc_mir/borrow_check/nll/region_infer/dfs.rs diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index 7231fe431729..a22dd1fecece 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -326,7 +326,7 @@ impl SparseBitMatrix { } /// True if `sub` is a subset of `sup` - pub fn subset(&self, sub: R, sup: R) -> bool { + pub fn is_subset(&self, sub: R, sup: R) -> bool { sub == sup || { let bit_set_sub = &self.vector[sub]; let bit_set_sup = &self.vector[sup]; diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs deleted file mode 100644 index f68394d61498..000000000000 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ /dev/null @@ -1,265 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Module defining the `dfs` method on `RegionInferenceContext`, along with -//! its associated helper traits. - -use borrow_check::nll::universal_regions::UniversalRegions; -use borrow_check::nll::region_infer::RegionInferenceContext; -use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements, - RegionValues}; -use syntax::codemap::Span; -use rustc::mir::{Location, Mir}; -use rustc::ty::RegionVid; -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::Idx; - -pub(super) struct DfsStorage { - stack: Vec, - visited: BitVector, -} - -impl<'tcx> RegionInferenceContext<'tcx> { - /// Creates dfs storage for use by dfs; this should be shared - /// across as many calls to dfs as possible to amortize allocation - /// costs. - pub(super) fn new_dfs_storage(&self) -> DfsStorage { - let num_elements = self.elements.num_elements(); - DfsStorage { - stack: vec![], - visited: BitVector::new(num_elements), - } - } - - /// Function used to satisfy or test a `R1: R2 @ P` - /// constraint. The core idea is that it performs a DFS starting - /// from `P`. The precise actions *during* that DFS depend on the - /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more - /// details. - /// - /// Returns: - /// - /// - `Ok(true)` if the walk was completed and something changed - /// along the way; - /// - `Ok(false)` if the walk was completed with no changes; - /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the - /// value that `op` returned. - #[inline(never)] // ensure dfs is identifiable in profiles - pub(super) fn dfs( - &self, - mir: &Mir<'tcx>, - dfs: &mut DfsStorage, - mut op: C, - ) -> Result - where - C: DfsOp, - { - let mut changed = false; - - dfs.visited.clear(); - dfs.stack.push(op.start_point()); - while let Some(p) = dfs.stack.pop() { - let point_index = self.elements.index(p); - - if !op.source_region_contains(point_index) { - debug!(" not in from-region"); - continue; - } - - if !dfs.visited.insert(point_index.index()) { - debug!(" already visited"); - continue; - } - - let new = op.add_to_target_region(point_index)?; - changed |= new; - - let block_data = &mir[p.block]; - - let start_stack_len = dfs.stack.len(); - - if p.statement_index < block_data.statements.len() { - dfs.stack.push(Location { - statement_index: p.statement_index + 1, - ..p - }); - } else { - dfs.stack.extend( - block_data - .terminator() - .successors() - .map(|&basic_block| Location { - statement_index: 0, - block: basic_block, - }), - ); - } - - if dfs.stack.len() == start_stack_len { - // If we reach the END point in the graph, then copy - // over any skolemized end points in the `from_region` - // and make sure they are included in the `to_region`. - changed |= op.add_universal_regions_outlived_by_source_to_target()?; - } - } - - Ok(changed) - } -} - -/// Customizes the operation of the `dfs` function. This function is -/// used during inference to satisfy a `R1: R2 @ P` constraint. -pub(super) trait DfsOp { - /// If this op stops the walk early, what type does it propagate? - type Early; - - /// Returns the point from which to start the DFS. - fn start_point(&self) -> Location; - - /// Returns true if the source region contains the given point. - fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool; - - /// Adds the given point to the target region, returning true if - /// something has changed. Returns `Err` if we should abort the - /// walk early. - fn add_to_target_region( - &mut self, - point_index: RegionElementIndex, - ) -> Result; - - /// Adds all universal regions in the source region to the target region, returning - /// true if something has changed. - fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result; -} - -/// Used during inference to enforce a `R1: R2 @ P` constraint. For -/// each point Q we reach along the DFS, we check if Q is in R2 (the -/// "source region"). If not, we stop the walk. Otherwise, we add Q to -/// R1 (the "target region") and continue to Q's successors. If we -/// reach the end of the graph, then we add any universal regions from -/// R2 into R1. -pub(super) struct CopyFromSourceToTarget<'v> { - pub source_region: RegionVid, - pub target_region: RegionVid, - pub inferred_values: &'v mut RegionValues, - pub constraint_point: Location, - pub constraint_span: Span, -} - -impl<'v> DfsOp for CopyFromSourceToTarget<'v> { - /// We never stop the walk early. - type Early = !; - - fn start_point(&self) -> Location { - self.constraint_point - } - - fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { - self.inferred_values - .contains(self.source_region, point_index) - } - - fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result { - Ok(self.inferred_values.add_due_to_outlives( - self.source_region, - self.target_region, - point_index, - self.constraint_point, - self.constraint_span, - )) - } - - fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result { - Ok(self.inferred_values.add_universal_regions_outlived_by( - self.source_region, - self.target_region, - self.constraint_point, - self.constraint_span, - )) - } -} - -/// Used after inference to *test* a `R1: R2 @ P` constraint. For -/// each point Q we reach along the DFS, we check if Q in R2 is also -/// contained in R1. If not, we abort the walk early with an `Err` -/// condition. Similarly, if we reach the end of the graph and find -/// that R1 contains some universal region that R2 does not contain, -/// we abort the walk early. -pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> { - pub source_region: RegionVid, - pub target_region: RegionVid, - pub elements: &'v RegionValueElements, - pub universal_regions: &'v UniversalRegions<'tcx>, - pub inferred_values: &'v RegionValues, - pub constraint_point: Location, -} - -impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { - /// The element that was not found within R2. - type Early = RegionElementIndex; - - fn start_point(&self) -> Location { - self.constraint_point - } - - fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { - self.inferred_values - .contains(self.source_region, point_index) - } - - fn add_to_target_region( - &mut self, - point_index: RegionElementIndex, - ) -> Result { - if !self.inferred_values - .contains(self.target_region, point_index) - { - return Err(point_index); - } - - Ok(false) - } - - fn add_universal_regions_outlived_by_source_to_target( - &mut self, - ) -> Result { - // For all `ur_in_source` in `source_region`. - for ur_in_source in self.inferred_values - .universal_regions_outlived_by(self.source_region) - { - // Check that `target_region` outlives `ur_in_source`. - - // If `ur_in_source` is a member of `target_region`, OK. - // - // (This is implied by the loop below, actually, just an - // irresistible micro-opt. Mm. Premature optimization. So - // tasty.) - if self.inferred_values - .contains(self.target_region, ur_in_source) - { - continue; - } - - // If there is some other element X such that `target_region: X` and - // `X: ur_in_source`, OK. - if self.inferred_values - .universal_regions_outlived_by(self.target_region) - .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source)) - { - continue; - } - - // Otherwise, not known to be true. - return Err(self.elements.index(ur_in_source)); - } - - Ok(false) - } -} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index b9459bb90239..2fdb7d63cb5d 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -11,15 +11,17 @@ use super::universal_regions::UniversalRegions; use borrow_check::nll::region_infer::values::ToElementIndex; use rustc::hir::def_id::DefId; +use rustc::infer::error_reporting::nice_region_error::NiceRegionError; +use rustc::infer::region_constraints::{GenericKind, VarInfos}; use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; -use rustc::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc::infer::region_constraints::{GenericKind, VarInfos}; -use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - Local, Location, Mir}; +use rustc::mir::{ + ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location, + Mir, +}; use rustc::traits::ObligationCause; use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; @@ -31,8 +33,6 @@ use syntax::ast; use syntax_pos::Span; mod annotation; -mod dfs; -use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource}; mod dump_mir; mod graphviz; mod values; @@ -422,9 +422,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> Option> { assert!(self.inferred_values.is_none(), "values already inferred"); - let dfs_storage = &mut self.new_dfs_storage(); - - self.propagate_constraints(mir, dfs_storage); + self.propagate_constraints(mir); // If this is a closure, we can propagate unsatisfied // `outlives_requirements` to our creator, so create a vector @@ -437,13 +435,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { None }; - self.check_type_tests( - infcx, - mir, - dfs_storage, - mir_def_id, - outlives_requirements.as_mut(), - ); + self.check_type_tests(infcx, mir, mir_def_id, outlives_requirements.as_mut()); self.check_universal_regions(infcx, mir_def_id, outlives_requirements.as_mut()); @@ -464,18 +456,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. - fn propagate_constraints(&mut self, mir: &Mir<'tcx>, dfs_storage: &mut dfs::DfsStorage) { + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { self.dependency_map = Some(self.build_dependency_map()); - let inferred_values = self.compute_region_values(mir, dfs_storage); + let inferred_values = self.compute_region_values(mir); self.inferred_values = Some(inferred_values); } #[inline(never)] // ensure dfs is identifiable in profiles - fn compute_region_values( - &self, - mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, - ) -> RegionValues { + fn compute_region_values(&self, _mir: &Mir<'tcx>) -> RegionValues { debug!("compute_region_values()"); debug!("compute_region_values: constraints={:#?}", { let mut constraints: Vec<_> = self.constraints.iter().collect(); @@ -502,21 +490,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let constraint = &self.constraints[constraint_idx]; debug!("propagate_constraints: constraint={:?}", constraint); - // Grow the value as needed to accommodate the - // outlives constraint. - let Ok(made_changes) = self.dfs( - mir, - dfs_storage, - CopyFromSourceToTarget { - source_region: constraint.sub, - target_region: constraint.sup, - inferred_values: &mut inferred_values, - constraint_point: constraint.point, - constraint_span: constraint.span, - }, - ); - - if made_changes { + if inferred_values.add_region(constraint.sup, constraint.sub) { debug!("propagate_constraints: sub={:?}", constraint.sub); debug!("propagate_constraints: sup={:?}", constraint.sup); @@ -561,7 +535,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec>>, ) { @@ -570,13 +543,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if self.eval_region_test( - mir, - dfs_storage, - type_test.point, - type_test.lower_bound, - &type_test.test, - ) { + if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) { continue; } @@ -833,7 +800,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn eval_region_test( &self, mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, point: Location, lower_bound: RegionVid, test: &RegionTest, @@ -846,27 +812,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { match test { RegionTest::IsOutlivedByAllRegionsIn(regions) => regions .iter() - .all(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)), + .all(|&r| self.eval_outlives(mir, r, lower_bound, point)), RegionTest::IsOutlivedByAnyRegionIn(regions) => regions .iter() - .any(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)), + .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), RegionTest::Any(tests) => tests .iter() - .any(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)), + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), RegionTest::All(tests) => tests .iter() - .all(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)), + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), } } // Evaluate whether `sup_region: sub_region @ point`. fn eval_outlives( &self, - mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, + _mir: &Mir<'tcx>, sup_region: RegionVid, sub_region: RegionVid, point: Location, @@ -876,36 +841,43 @@ impl<'tcx> RegionInferenceContext<'tcx> { sup_region, sub_region, point ); - // Roughly speaking, do a DFS of all region elements reachable - // from `point` contained in `sub_region`. If any of those are - // *not* present in `sup_region`, the DFS will abort early and - // yield an `Err` result. - match self.dfs( - mir, - dfs_storage, - TestTargetOutlivesSource { - source_region: sub_region, - target_region: sup_region, - constraint_point: point, - elements: &self.elements, - universal_regions: &self.universal_regions, - inferred_values: self.inferred_values.as_ref().unwrap(), - }, - ) { - Ok(_) => { - debug!("eval_outlives: true"); - true - } + let inferred_values = self.inferred_values.as_ref().expect("values for regions not yet inferred"); - Err(elem) => { - debug!( - "eval_outlives: false because `{:?}` is not present in `{:?}`", - self.elements.to_element(elem), - sup_region - ); - false - } + debug!( + "eval_outlives: sup_region's value = {:?}", + inferred_values.region_value_str(sup_region), + ); + debug!( + "eval_outlives: sub_region's value = {:?}", + inferred_values.region_value_str(sub_region), + ); + + // Both the `sub_region` and `sup_region` consist of the union + // of some number of universal regions (along with the union + // of various points in the CFG; ignore those points for + // now). Therefore, the sup-region outlives the sub-region if, + // for each universal region R1 in the sub-region, there + // exists some region R2 in the sup-region that outlives R1. + let universal_outlives = + inferred_values.universal_regions_outlived_by(sub_region) + .all(|r1| { + inferred_values.universal_regions_outlived_by(sup_region) + .any(|r2| self.universal_regions.outlives(r2, r1)) + }); + + if !universal_outlives { + return false; } + + // Now we have to compare all the points in the sub region and make + // sure they exist in the sup region. + + if self.universal_regions.is_universal_region(sup_region) { + // Micro-opt: universal regions contain all points. + return true; + } + + inferred_values.contains_points(sup_region, sub_region) } /// Once regions have been propagated, this method is used to see diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 126a34831a88..3e2d9d956e78 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -17,7 +17,6 @@ use rustc::mir::{BasicBlock, Location, Mir}; use rustc::ty::RegionVid; use std::fmt::Debug; use std::rc::Rc; -use syntax::codemap::Span; use super::Cause; @@ -74,11 +73,6 @@ impl RegionValueElements { (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions)) } - /// Iterates over the `RegionElementIndex` for all points in the CFG. - pub(super) fn all_universal_region_indices(&self) -> impl Iterator { - (0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i)) - } - /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents. pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement { debug!("to_element(i={:?})", i); @@ -244,6 +238,12 @@ impl RegionValues { self.add_internal(r, i, |_| cause.clone()) } + /// Add all elements in `r_from` to `r_to` (because e.g. `r_to: + /// r_from`). + pub(super) fn add_region(&mut self, r_to: RegionVid, r_from: RegionVid) -> bool { + self.matrix.merge(r_from, r_to) + } + /// Internal method to add an element to a region. /// /// Takes a "lazy" cause -- this function will return the cause, but it will only @@ -278,60 +278,22 @@ impl RegionValues { } } - /// Adds `elem` to `to_region` because of a relation: - /// - /// to_region: from_region @ constraint_location - /// - /// that was added by the cod at `constraint_span`. - pub(super) fn add_due_to_outlives( - &mut self, - from_region: RegionVid, - to_region: RegionVid, - elem: T, - _constraint_location: Location, - _constraint_span: Span, - ) -> bool { - let elem = self.elements.index(elem); - self.add_internal(to_region, elem, |causes| causes[&(from_region, elem)]) - } - - /// Adds all the universal regions outlived by `from_region` to - /// `to_region`. - pub(super) fn add_universal_regions_outlived_by( - &mut self, - from_region: RegionVid, - to_region: RegionVid, - constraint_location: Location, - constraint_span: Span, - ) -> bool { - // We could optimize this by improving `SparseBitMatrix::merge` so - // it does not always merge an entire row. That would - // complicate causal tracking though. - debug!( - "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", - from_region, to_region - ); - let mut changed = false; - for elem in self.elements.all_universal_region_indices() { - if self.contains(from_region, elem) { - changed |= self.add_due_to_outlives( - from_region, - to_region, - elem, - constraint_location, - constraint_span, - ); - } - } - changed - } - /// True if the region `r` contains the given element. pub(super) fn contains(&self, r: RegionVid, elem: E) -> bool { let i = self.elements.index(elem); self.matrix.contains(r, i) } + /// True if `sup_region` contains all the CFG points that + /// `sub_region` contains. Ignores universal regions. + pub(super) fn contains_points(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { + // This could be done faster by comparing the bitsets. But I + // am lazy. + self.element_indices_contained_in(sub_region) + .skip_while(|&i| self.elements.to_universal_region(i).is_some()) + .all(|e| self.contains(sup_region, e)) + } + /// Iterate over the value of the region `r`, yielding up element /// indices. You may prefer `universal_regions_outlived_by` or /// `elements_contained_in`. diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs index 728c84695eac..1a417b1e28c2 100644 --- a/src/test/ui/nll/get_default.rs +++ b/src/test/ui/nll/get_default.rs @@ -30,8 +30,9 @@ fn ok(map: &mut Map) -> &String { return v; } None => { - map.set(String::new()); // Just AST errors here + map.set(String::new()); // Ideally, this would not error. //~^ ERROR borrowed as immutable (Ast) + //~| ERROR borrowed as immutable (Mir) } } } @@ -47,8 +48,9 @@ fn err(map: &mut Map) -> &String { return v; } None => { - map.set(String::new()); // Just AST errors here + map.set(String::new()); // Ideally, just AST would error here //~^ ERROR borrowed as immutable (Ast) + //~| ERROR borrowed as immutable (Mir) } } } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr index 064fd38b8725..dd69e18652c9 100644 --- a/src/test/ui/nll/get_default.stderr +++ b/src/test/ui/nll/get_default.stderr @@ -4,14 +4,14 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm LL | match map.get() { | --- immutable borrow occurs here ... -LL | map.set(String::new()); // Just AST errors here +LL | map.set(String::new()); // Ideally, this would not error. | ^^^ mutable borrow occurs here ... LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) - --> $DIR/get_default.rs:44:17 + --> $DIR/get_default.rs:45:17 | LL | match map.get() { | --- immutable borrow occurs here @@ -23,19 +23,61 @@ LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) - --> $DIR/get_default.rs:50:17 + --> $DIR/get_default.rs:51:17 | LL | match map.get() { | --- immutable borrow occurs here ... -LL | map.set(String::new()); // Just AST errors here +LL | map.set(String::new()); // Ideally, just AST would error here | ^^^ mutable borrow occurs here ... LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) - --> $DIR/get_default.rs:44:17 + --> $DIR/get_default.rs:33:17 + | +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | map.set(String::new()); // Ideally, this would not error. + | ^^^ mutable borrow occurs here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1... + --> $DIR/get_default.rs:26:1 + | +LL | / fn ok(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:51:17 + | +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | map.set(String::new()); // Ideally, just AST would error here + | ^^^ mutable borrow occurs here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1... + --> $DIR/get_default.rs:41:1 + | +LL | / fn err(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:45:17 | LL | match map.get() { | --- immutable borrow occurs here @@ -46,6 +88,6 @@ LL | map.set(String::new()); // Both AST and MIR error here LL | return v; | - borrow later used here -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0502`.