From e025306fa050ac593bff366eb1c9bff7bfd00b1d Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 22 Oct 2022 02:48:36 +0000
Subject: [PATCH] Don't ICE on regions from anonymous_lifetime_in_impl_trait

---
 .../src/diagnostics/region_name.rs            | 100 ++++++++++++++++--
 .../ui/borrowck/anonymous-region-in-apit.rs   |  12 +++
 .../borrowck/anonymous-region-in-apit.stderr  |  16 +++
 3 files changed, 120 insertions(+), 8 deletions(-)
 create mode 100644 src/test/ui/borrowck/anonymous-region-in-apit.rs
 create mode 100644 src/test/ui/borrowck/anonymous-region-in-apit.stderr

diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 4d251cf7ac752..c044dbaba47e2 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -251,7 +251,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
             .or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(fr))
             .or_else(|| self.give_name_if_anonymous_region_appears_in_output(fr))
             .or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr))
-            .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr));
+            .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr))
+            .or_else(|| self.give_name_if_anonymous_region_appears_in_arg_position_impl_trait(fr));
 
         if let Some(ref value) = value {
             self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone());
@@ -869,13 +870,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
             return None;
         }
 
-        let mut found = false;
-        tcx.fold_regions(tcx.type_of(region_parent), |r: ty::Region<'tcx>, _| {
-            if *r == ty::ReEarlyBound(region) {
-                found = true;
-            }
-            r
-        });
+        let found = tcx
+            .any_free_region_meets(&tcx.type_of(region_parent), |r| *r == ty::ReEarlyBound(region));
 
         Some(RegionName {
             name: self.synthesize_region_name(),
@@ -888,4 +884,92 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
             ),
         })
     }
+
+    fn give_name_if_anonymous_region_appears_in_arg_position_impl_trait(
+        &self,
+        fr: RegionVid,
+    ) -> Option<RegionName> {
+        let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else {
+            return None;
+        };
+        if region.has_name() {
+            return None;
+        };
+
+        let predicates = self
+            .infcx
+            .tcx
+            .predicates_of(self.body.source.def_id())
+            .instantiate_identity(self.infcx.tcx)
+            .predicates;
+
+        if let Some(upvar_index) = self
+            .regioncx
+            .universal_regions()
+            .defining_ty
+            .upvar_tys()
+            .position(|ty| self.any_param_predicate_mentions(&predicates, ty, region))
+        {
+            let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region(
+                self.infcx.tcx,
+                &self.upvars,
+                upvar_index,
+            );
+            let region_name = self.synthesize_region_name();
+
+            Some(RegionName {
+                name: region_name,
+                source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
+            })
+        } else if let Some(arg_index) = self
+            .regioncx
+            .universal_regions()
+            .unnormalized_input_tys
+            .iter()
+            .position(|ty| self.any_param_predicate_mentions(&predicates, *ty, region))
+        {
+            let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region(
+                self.body,
+                &self.local_names,
+                arg_index,
+            );
+            let region_name = self.synthesize_region_name();
+
+            Some(RegionName {
+                name: region_name,
+                source: RegionNameSource::AnonRegionFromArgument(
+                    RegionNameHighlight::CannotMatchHirTy(arg_span, arg_name?.to_string()),
+                ),
+            })
+        } else {
+            None
+        }
+    }
+
+    fn any_param_predicate_mentions(
+        &self,
+        predicates: &[ty::Predicate<'tcx>],
+        ty: Ty<'tcx>,
+        region: ty::EarlyBoundRegion,
+    ) -> bool {
+        let tcx = self.infcx.tcx;
+        ty.walk().any(|arg| {
+            if let ty::GenericArgKind::Type(ty) = arg.unpack()
+                && let ty::Param(_) = ty.kind()
+            {
+                predicates.iter().any(|pred| {
+                    match pred.kind().skip_binder() {
+                        ty::PredicateKind::Trait(data) if data.self_ty() == ty => {}
+                        ty::PredicateKind::Projection(data) if data.projection_ty.self_ty() == ty => {}
+                        _ => return false,
+                    }
+                    tcx.any_free_region_meets(pred, |r| {
+                        *r == ty::ReEarlyBound(region)
+                    })
+                })
+            } else {
+                false
+            }
+        })
+    }
 }
diff --git a/src/test/ui/borrowck/anonymous-region-in-apit.rs b/src/test/ui/borrowck/anonymous-region-in-apit.rs
new file mode 100644
index 0000000000000..7799a7cb151de
--- /dev/null
+++ b/src/test/ui/borrowck/anonymous-region-in-apit.rs
@@ -0,0 +1,12 @@
+#![feature(anonymous_lifetime_in_impl_trait)]
+
+trait Foo<T> {
+    fn bar(self, baz: T);
+}
+
+fn qux(foo: impl Foo<&str>) {
+    |baz: &str| foo.bar(baz);
+    //~^ ERROR borrowed data escapes outside of closure
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/anonymous-region-in-apit.stderr b/src/test/ui/borrowck/anonymous-region-in-apit.stderr
new file mode 100644
index 0000000000000..9e100f8ac3c50
--- /dev/null
+++ b/src/test/ui/borrowck/anonymous-region-in-apit.stderr
@@ -0,0 +1,16 @@
+error[E0521]: borrowed data escapes outside of closure
+  --> $DIR/anonymous-region-in-apit.rs:8:17
+   |
+LL | fn qux(foo: impl Foo<&str>) {
+   |        --- lifetime `'2` appears in the type of `foo`
+LL |     |baz: &str| foo.bar(baz);
+   |      ---  -     ^^^^^^^^^^^^
+   |      |    |     |
+   |      |    |     `baz` escapes the closure body here
+   |      |    |     argument requires that `'1` must outlive `'2`
+   |      |    let's call the lifetime of this reference `'1`
+   |      `baz` is a reference that is only valid in the closure body
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0521`.