about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCamille GILLOT <gillot.camille@gmail.com>2022-05-27 11:02:11 +0200
committerCamille GILLOT <gillot.camille@gmail.com>2022-06-24 17:59:19 +0200
commitb1d53a42dc2517ec873c97752bc23c9ae88c9390 (patch)
treeba474af3881b0b38cc45184999fddfbfdaed3ba4
parent1bc802e990a3393731b19b30f7fad1794b478df0 (diff)
downloadrust-b1d53a42dc2517ec873c97752bc23c9ae88c9390.tar.gz
rust-b1d53a42dc2517ec873c97752bc23c9ae88c9390.zip
Fail gracefully when encountering an HRTB in APIT.
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs71
-rw-r--r--src/test/ui/impl-trait/universal_wrong_hrtb.rs8
-rw-r--r--src/test/ui/impl-trait/universal_wrong_hrtb.stderr14
4 files changed, 87 insertions, 8 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 4d4d4a28499..dc88d866988 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -721,7 +721,7 @@ impl<'hir> WherePredicate<'hir> {
     }
 }
 
-#[derive(Debug, HashStable_Generic, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
 pub enum PredicateOrigin {
     WhereClause,
     GenericParam,
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 700d7c3bfb6..f45b345487f 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -230,6 +230,13 @@ enum Scope<'a> {
         /// In some cases not allowing late bounds allows us to avoid ICEs.
         /// This is almost ways set to true.
         allow_late_bound: bool,
+
+        /// If this binder comes from a where clause, specify how it was created.
+        /// This is used to diagnose inaccessible lifetimes in APIT:
+        /// ```ignore (illustrative)
+        /// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+        /// ```
+        where_bound_origin: Option<hir::PredicateOrigin>,
     },
 
     /// Lifetimes introduced by a fn are scoped to the call-site for that fn,
@@ -301,8 +308,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 opaque_type_parent,
                 scope_type,
                 hir_id,
-                s: _,
                 allow_late_bound,
+                where_bound_origin,
+                s: _,
             } => f
                 .debug_struct("Binder")
                 .field("lifetimes", lifetimes)
@@ -311,8 +319,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 .field("opaque_type_parent", opaque_type_parent)
                 .field("scope_type", scope_type)
                 .field("hir_id", hir_id)
-                .field("s", &"..")
                 .field("allow_late_bound", allow_late_bound)
+                .field("where_bound_origin", where_bound_origin)
+                .field("s", &"..")
                 .finish(),
             Scope::Body { id, s: _ } => {
                 f.debug_struct("Body").field("id", id).field("s", &"..").finish()
@@ -701,6 +710,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: false,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, move |_old_scope, this| {
                     intravisit::walk_fn(this, fk, fd, b, s, hir_id)
@@ -829,6 +839,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     scope_type: BinderScopeType::Normal,
                     s: ROOT_SCOPE,
                     allow_late_bound: false,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |old_scope, this| {
                     this.check_lifetime_params(old_scope, &generics.params);
@@ -896,6 +907,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: false,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |old_scope, this| {
                     // a bare fn has no bounds, so everything
@@ -1091,6 +1103,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                             opaque_type_parent: false,
                             scope_type: BinderScopeType::Normal,
                             allow_late_bound: false,
+                            where_bound_origin: None,
                         };
                         this.with(scope, |_old_scope, this| {
                             this.visit_generics(generics);
@@ -1112,6 +1125,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         opaque_type_parent: false,
                         scope_type: BinderScopeType::Normal,
                         allow_late_bound: false,
+                        where_bound_origin: None,
                     };
                     self.with(scope, |_old_scope, this| {
                         let scope = Scope::TraitRefBoundary { s: this.scope };
@@ -1172,6 +1186,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: false,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |old_scope, this| {
                     this.check_lifetime_params(old_scope, &generics.params);
@@ -1242,6 +1257,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |old_scope, this| {
                     this.check_lifetime_params(old_scope, &generics.params);
@@ -1356,6 +1372,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         ref bounded_ty,
                         bounds,
                         ref bound_generic_params,
+                        origin,
                         ..
                     }) => {
                         let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) =
@@ -1387,6 +1404,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                             opaque_type_parent: false,
                             scope_type: BinderScopeType::Normal,
                             allow_late_bound: true,
+                            where_bound_origin: Some(origin),
                         };
                         this.with(scope, |old_scope, this| {
                             this.check_lifetime_params(old_scope, &bound_generic_params);
@@ -1461,6 +1479,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: false,
                     scope_type,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |_, this| {
                     intravisit::walk_param_bound(this, bound);
@@ -1514,6 +1533,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             opaque_type_parent: false,
             scope_type,
             allow_late_bound: true,
+            where_bound_origin: None,
         };
         self.with(scope, |old_scope, this| {
             this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
@@ -2207,6 +2227,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             track_lifetime_uses: false,
             scope_type: BinderScopeType::Normal,
             allow_late_bound: true,
+            where_bound_origin: None,
         };
         self.with(scope, move |old_scope, this| {
             this.check_lifetime_params(old_scope, &generics.params);
@@ -2321,12 +2342,48 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             }
 
             self.insert_lifetime(lifetime_ref, def);
-        } else {
-            self.tcx.sess.delay_span_bug(
-                lifetime_ref.span,
-                &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
-            );
+            return;
         }
+
+        // We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
+        // AST-based resolution does not care for impl-trait desugaring, which are the
+        // responibility of lowering.  This may create a mismatch between the resolution
+        // AST found (`region_def_id`) which points to HRTB, and what HIR allows.
+        // ```
+        // fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+        // ```
+        //
+        // In such case, walk back the binders to diagnose it properly.
+        let mut scope = self.scope;
+        loop {
+            match *scope {
+                Scope::Binder {
+                    where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
+                } => {
+                    let mut err = self.tcx.sess.struct_span_err(
+                        lifetime_ref.span,
+                        "`impl Trait` can only mention lifetimes bound at the fn or impl level",
+                    );
+                    err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
+                    err.emit();
+                    return;
+                }
+                Scope::Root => break,
+                Scope::Binder { s, .. }
+                | Scope::Body { s, .. }
+                | Scope::Elision { s, .. }
+                | Scope::ObjectLifetimeDefault { s, .. }
+                | Scope::Supertrait { s, .. }
+                | Scope::TraitRefBoundary { s, .. } => {
+                    scope = s;
+                }
+            }
+        }
+
+        self.tcx.sess.delay_span_bug(
+            lifetime_ref.span,
+            &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
+        );
     }
 
     fn visit_segment_args(
diff --git a/src/test/ui/impl-trait/universal_wrong_hrtb.rs b/src/test/ui/impl-trait/universal_wrong_hrtb.rs
new file mode 100644
index 00000000000..b9551c2ceb0
--- /dev/null
+++ b/src/test/ui/impl-trait/universal_wrong_hrtb.rs
@@ -0,0 +1,8 @@
+trait Trait<'a> {
+    type Assoc;
+}
+
+fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+//~^ ERROR `impl Trait` can only mention lifetimes bound at the fn or impl level
+
+fn main() {}
diff --git a/src/test/ui/impl-trait/universal_wrong_hrtb.stderr b/src/test/ui/impl-trait/universal_wrong_hrtb.stderr
new file mode 100644
index 00000000000..37eb8dfa1a1
--- /dev/null
+++ b/src/test/ui/impl-trait/universal_wrong_hrtb.stderr
@@ -0,0 +1,14 @@
+error: `impl Trait` can only mention lifetimes bound at the fn or impl level
+  --> $DIR/universal_wrong_hrtb.rs:5:73
+   |
+LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+   |                                                                         ^^
+   |
+note: lifetime declared here
+  --> $DIR/universal_wrong_hrtb.rs:5:39
+   |
+LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+   |                                       ^^
+
+error: aborting due to previous error
+