about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_data_structures/bitvec.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/dfs.rs265
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs134
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/values.rs70
-rw-r--r--src/test/ui/nll/get_default.rs6
-rw-r--r--src/test/ui/nll/get_default.stderr54
6 files changed, 122 insertions, 409 deletions
diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs
index 7231fe43172..a22dd1fecec 100644
--- a/src/librustc_data_structures/bitvec.rs
+++ b/src/librustc_data_structures/bitvec.rs
@@ -326,7 +326,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
     }
 
     /// 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 f68394d6149..00000000000
--- 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 <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.
-
-//! 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<Location>,
-    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<C>(
-        &self,
-        mir: &Mir<'tcx>,
-        dfs: &mut DfsStorage,
-        mut op: C,
-    ) -> Result<bool, C::Early>
-    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<bool, Self::Early>;
-
-    /// 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<bool, Self::Early>;
-}
-
-/// 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<bool, !> {
-        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<bool, !> {
-        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<bool, RegionElementIndex> {
-        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<bool, RegionElementIndex> {
-        // 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 b9459bb9023..2fdb7d63cb5 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<ClosureRegionRequirements<'gcx>> {
         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<ClosureOutlivesRequirement<'gcx>>>,
     ) {
@@ -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 126a34831a8..3e2d9d956e7 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<Item = RegionElementIndex> {
-        (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<T: ToElementIndex>(
-        &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<E: ToElementIndex>(&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 728c84695ea..1a417b1e28c 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 064fd38b872..dd69e18652c 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`.