about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-06-28 19:37:21 -0400
committerMichael Goulet <michael@errs.io>2024-06-28 19:40:31 -0400
commit3bc32472008387ab2adca98dcfd0bef6207357c1 (patch)
tree004ba8b336c13bad906929c79e55e4cb2f658832
parent036b38ced36b0ed16579f95b4647ba7424f6b1bc (diff)
downloadrust-3bc32472008387ab2adca98dcfd0bef6207357c1.tar.gz
rust-3bc32472008387ab2adca98dcfd0bef6207357c1.zip
Move binder and polarity parsing into parse_generic_ty_bound
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs98
-rw-r--r--tests/ui/higher-ranked/erroneous-lifetime-bound.rs5
-rw-r--r--tests/ui/higher-ranked/erroneous-lifetime-bound.stderr25
-rw-r--r--tests/ui/impl-trait/precise-capturing/bound-modifiers.rs25
-rw-r--r--tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr87
7 files changed, 204 insertions, 40 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e0c70884fee..ac66edeb95e 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2327,7 +2327,7 @@ impl<'a> Parser<'a> {
         let before = self.prev_token.clone();
         let binder = if self.check_keyword(kw::For) {
             let lo = self.token.span;
-            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
             let span = lo.to(self.prev_token.span);
 
             self.psess.gated_spans.gate(sym::closure_lifetime_binder, span);
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index fde16ac957d..10c7715c7dc 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -457,7 +457,7 @@ impl<'a> Parser<'a> {
         // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
         // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
         // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
-        let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+        let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
 
         // Parse type with mandatory colon and (possibly empty) bounds,
         // or with mandatory equality sign and the second type.
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index d2043c353fe..1e5b227aaa9 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -18,7 +18,7 @@ use rustc_ast::{
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{Span, Symbol};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
 use thin_vec::{thin_vec, ThinVec};
 
 #[derive(Copy, Clone, PartialEq)]
@@ -280,7 +280,7 @@ impl<'a> Parser<'a> {
             // Function pointer type or bound list (trait object type) starting with a poly-trait.
             //   `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
             //   `for<'lt> Trait1<'lt> + Trait2 + 'a`
-            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
             if self.check_fn_front_matter(false, Case::Sensitive) {
                 self.parse_ty_bare_fn(
                     lo,
@@ -833,12 +833,9 @@ impl<'a> Parser<'a> {
         let lo = self.token.span;
         let leading_token = self.prev_token.clone();
         let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis));
-        let inner_lo = self.token.span;
 
-        let modifiers = self.parse_trait_bound_modifiers()?;
         let bound = if self.token.is_lifetime() {
-            self.error_lt_bound_with_modifiers(modifiers);
-            self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
+            self.parse_generic_lt_bound(lo, has_parens)?
         } else if self.eat_keyword(kw::Use) {
             // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
             // lifetimes and ident params (including SelfUpper). These are validated later
@@ -848,7 +845,7 @@ impl<'a> Parser<'a> {
             let (args, args_span) = self.parse_precise_capturing_args()?;
             GenericBound::Use(args, use_span.to(args_span))
         } else {
-            self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)?
+            self.parse_generic_ty_bound(lo, has_parens, &leading_token)?
         };
 
         Ok(bound)
@@ -858,50 +855,64 @@ impl<'a> Parser<'a> {
     /// ```ebnf
     /// LT_BOUND = LIFETIME
     /// ```
-    fn parse_generic_lt_bound(
-        &mut self,
-        lo: Span,
-        inner_lo: Span,
-        has_parens: bool,
-    ) -> PResult<'a, GenericBound> {
-        let bound = GenericBound::Outlives(self.expect_lifetime());
+    fn parse_generic_lt_bound(&mut self, lo: Span, has_parens: bool) -> PResult<'a, GenericBound> {
+        let lt = self.expect_lifetime();
+        let bound = GenericBound::Outlives(lt);
         if has_parens {
             // FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
             // possibly introducing `GenericBound::Paren(P<GenericBound>)`?
-            self.recover_paren_lifetime(lo, inner_lo)?;
+            self.recover_paren_lifetime(lo, lt.ident.span)?;
         }
         Ok(bound)
     }
 
     /// Emits an error if any trait bound modifiers were present.
-    fn error_lt_bound_with_modifiers(&self, modifiers: TraitBoundModifiers) {
-        match modifiers.constness {
+    fn error_lt_bound_with_modifiers(
+        &self,
+        modifiers: TraitBoundModifiers,
+        binder_span: Option<Span>,
+    ) -> ErrorGuaranteed {
+        let TraitBoundModifiers { constness, asyncness, polarity } = modifiers;
+
+        match constness {
             BoundConstness::Never => {}
             BoundConstness::Always(span) | BoundConstness::Maybe(span) => {
-                self.dcx().emit_err(errors::ModifierLifetime {
-                    span,
-                    modifier: modifiers.constness.as_str(),
-                });
+                return self
+                    .dcx()
+                    .emit_err(errors::ModifierLifetime { span, modifier: constness.as_str() });
             }
         }
 
-        match modifiers.polarity {
+        match polarity {
             BoundPolarity::Positive => {}
             BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => {
-                self.dcx().emit_err(errors::ModifierLifetime {
-                    span,
-                    modifier: modifiers.polarity.as_str(),
-                });
+                return self
+                    .dcx()
+                    .emit_err(errors::ModifierLifetime { span, modifier: polarity.as_str() });
+            }
+        }
+
+        match asyncness {
+            BoundAsyncness::Normal => {}
+            BoundAsyncness::Async(span) => {
+                return self
+                    .dcx()
+                    .emit_err(errors::ModifierLifetime { span, modifier: asyncness.as_str() });
             }
         }
+
+        if let Some(span) = binder_span {
+            return self.dcx().emit_err(errors::ModifierLifetime { span, modifier: "for<...>" });
+        }
+
+        unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?")
     }
 
     /// Recover on `('lifetime)` with `(` already eaten.
-    fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
-        let inner_span = inner_lo.to(self.prev_token.span);
+    fn recover_paren_lifetime(&mut self, lo: Span, lt_span: Span) -> PResult<'a, ()> {
         self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
         let span = lo.to(self.prev_token.span);
-        let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(inner_span) {
+        let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(lt_span) {
             (Some(span), snippet)
         } else {
             (None, String::new())
@@ -916,7 +927,7 @@ impl<'a> Parser<'a> {
     /// If no modifiers are present, this does not consume any tokens.
     ///
     /// ```ebnf
-    /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["?" | "!"]
+    /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["async"] ["?" | "!"]
     /// ```
     fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
         let constness = if self.eat(&token::Tilde) {
@@ -970,15 +981,23 @@ impl<'a> Parser<'a> {
     /// TY_BOUND_NOPAREN = [TRAIT_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
     /// ```
     ///
-    /// For example, this grammar accepts `~const ?for<'a: 'b> m::Trait<'a>`.
+    /// For example, this grammar accepts `for<'a: 'b> ~const ?m::Trait<'a>`.
     fn parse_generic_ty_bound(
         &mut self,
         lo: Span,
         has_parens: bool,
-        modifiers: TraitBoundModifiers,
         leading_token: &Token,
     ) -> PResult<'a, GenericBound> {
-        let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+        let modifiers = self.parse_trait_bound_modifiers()?;
+        let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
+
+        // Recover erroneous lifetime bound with modifiers or binder.
+        // e.g. `T: for<'a> 'a` or `T: ~const 'a`.
+        if self.token.is_lifetime() {
+            let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span);
+            return self.parse_generic_lt_bound(lo, has_parens);
+        }
+
         let mut path = if self.token.is_keyword(kw::Fn)
             && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
             && let Some(path) = self.recover_path_from_fn()
@@ -1094,16 +1113,19 @@ impl<'a> Parser<'a> {
     }
 
     /// Optionally parses `for<$generic_params>`.
-    pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, ThinVec<GenericParam>> {
+    pub(super) fn parse_late_bound_lifetime_defs(
+        &mut self,
+    ) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> {
         if self.eat_keyword(kw::For) {
+            let lo = self.token.span;
             self.expect_lt()?;
             let params = self.parse_generic_params()?;
             self.expect_gt()?;
-            // We rely on AST validation to rule out invalid cases: There must not be type
-            // parameters, and the lifetime parameters must not have bounds.
-            Ok(params)
+            // We rely on AST validation to rule out invalid cases: There must not be
+            // type or const parameters, and parameters must not have bounds.
+            Ok((params, Some(lo.to(self.prev_token.span))))
         } else {
-            Ok(ThinVec::new())
+            Ok((ThinVec::new(), None))
         }
     }
 
diff --git a/tests/ui/higher-ranked/erroneous-lifetime-bound.rs b/tests/ui/higher-ranked/erroneous-lifetime-bound.rs
new file mode 100644
index 00000000000..b1720087b5f
--- /dev/null
+++ b/tests/ui/higher-ranked/erroneous-lifetime-bound.rs
@@ -0,0 +1,5 @@
+fn foo<T>() where T: for<'a> 'a {}
+//~^ ERROR `for<...>` may only modify trait bounds, not lifetime bounds
+//~| ERROR use of undeclared lifetime name `'a` [E0261]
+
+fn main() {}
diff --git a/tests/ui/higher-ranked/erroneous-lifetime-bound.stderr b/tests/ui/higher-ranked/erroneous-lifetime-bound.stderr
new file mode 100644
index 00000000000..5b104f45d23
--- /dev/null
+++ b/tests/ui/higher-ranked/erroneous-lifetime-bound.stderr
@@ -0,0 +1,25 @@
+error: `for<...>` may only modify trait bounds, not lifetime bounds
+  --> $DIR/erroneous-lifetime-bound.rs:1:25
+   |
+LL | fn foo<T>() where T: for<'a> 'a {}
+   |                         ^^^^
+
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/erroneous-lifetime-bound.rs:1:30
+   |
+LL | fn foo<T>() where T: for<'a> 'a {}
+   |                              ^^ undeclared lifetime
+   |
+   = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider making the bound lifetime-generic with a new `'a` lifetime
+   |
+LL | fn foo<T>() where for<'a> T: for<'a> 'a {}
+   |                   +++++++
+help: consider introducing lifetime `'a` here
+   |
+LL | fn foo<'a, T>() where T: for<'a> 'a {}
+   |        +++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0261`.
diff --git a/tests/ui/impl-trait/precise-capturing/bound-modifiers.rs b/tests/ui/impl-trait/precise-capturing/bound-modifiers.rs
new file mode 100644
index 00000000000..15f21882628
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/bound-modifiers.rs
@@ -0,0 +1,25 @@
+//@ edition: 2021
+
+#![feature(precise_capturing)]
+
+fn polarity() -> impl Sized + ?use<> {}
+//~^ ERROR expected identifier, found keyword `use`
+//~| ERROR cannot find trait `r#use` in this scope
+//~| WARN relaxing a default bound only does something for `?Sized`
+//~| WARN relaxing a default bound only does something for `?Sized`
+
+fn asyncness() -> impl Sized + async use<> {}
+//~^ ERROR expected identifier, found keyword `use`
+//~| ERROR cannot find trait `r#use` in this scope
+//~| ERROR async closures are unstable
+
+fn constness() -> impl Sized + const use<> {}
+//~^ ERROR expected identifier, found keyword `use`
+//~| ERROR cannot find trait `r#use` in this scope
+//~| ERROR const trait impls are experimental
+
+fn binder() -> impl Sized + for<'a> use<> {}
+//~^ ERROR expected identifier, found keyword `use`
+//~| ERROR cannot find trait `r#use` in this scope
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr b/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr
new file mode 100644
index 00000000000..4602225e7b9
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr
@@ -0,0 +1,87 @@
+error: expected identifier, found keyword `use`
+  --> $DIR/bound-modifiers.rs:5:32
+   |
+LL | fn polarity() -> impl Sized + ?use<> {}
+   |                                ^^^ expected identifier, found keyword
+
+error: expected identifier, found keyword `use`
+  --> $DIR/bound-modifiers.rs:11:38
+   |
+LL | fn asyncness() -> impl Sized + async use<> {}
+   |                                      ^^^ expected identifier, found keyword
+
+error: expected identifier, found keyword `use`
+  --> $DIR/bound-modifiers.rs:16:38
+   |
+LL | fn constness() -> impl Sized + const use<> {}
+   |                                      ^^^ expected identifier, found keyword
+
+error: expected identifier, found keyword `use`
+  --> $DIR/bound-modifiers.rs:21:37
+   |
+LL | fn binder() -> impl Sized + for<'a> use<> {}
+   |                                     ^^^ expected identifier, found keyword
+
+error[E0405]: cannot find trait `r#use` in this scope
+  --> $DIR/bound-modifiers.rs:5:32
+   |
+LL | fn polarity() -> impl Sized + ?use<> {}
+   |                                ^^^ not found in this scope
+
+error[E0405]: cannot find trait `r#use` in this scope
+  --> $DIR/bound-modifiers.rs:11:38
+   |
+LL | fn asyncness() -> impl Sized + async use<> {}
+   |                                      ^^^ not found in this scope
+
+error[E0405]: cannot find trait `r#use` in this scope
+  --> $DIR/bound-modifiers.rs:16:38
+   |
+LL | fn constness() -> impl Sized + const use<> {}
+   |                                      ^^^ not found in this scope
+
+error[E0405]: cannot find trait `r#use` in this scope
+  --> $DIR/bound-modifiers.rs:21:37
+   |
+LL | fn binder() -> impl Sized + for<'a> use<> {}
+   |                                     ^^^ not found in this scope
+
+error[E0658]: async closures are unstable
+  --> $DIR/bound-modifiers.rs:11:32
+   |
+LL | fn asyncness() -> impl Sized + async use<> {}
+   |                                ^^^^^
+   |
+   = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
+   = help: add `#![feature(async_closure)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: to use an async block, remove the `||`: `async {`
+
+error[E0658]: const trait impls are experimental
+  --> $DIR/bound-modifiers.rs:16:32
+   |
+LL | fn constness() -> impl Sized + const use<> {}
+   |                                ^^^^^
+   |
+   = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
+   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/bound-modifiers.rs:5:31
+   |
+LL | fn polarity() -> impl Sized + ?use<> {}
+   |                               ^^^^^^
+
+warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
+  --> $DIR/bound-modifiers.rs:5:31
+   |
+LL | fn polarity() -> impl Sized + ?use<> {}
+   |                               ^^^^^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 10 previous errors; 2 warnings emitted
+
+Some errors have detailed explanations: E0405, E0658.
+For more information about an error, try `rustc --explain E0405`.