about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-09-17 14:56:48 +1000
committerGitHub <noreply@github.com>2025-09-17 14:56:48 +1000
commit61f53585aae6f3bdada28a2ddd05b759b90150b8 (patch)
treee5ac0f735aef330b8da2e3b704dec11077933422
parent6473a0f02d6549dc25d8f98b3d06f80fa453fe49 (diff)
parente9270e3cba3da56d4d83ed74f648e53b041cb263 (diff)
downloadrust-61f53585aae6f3bdada28a2ddd05b759b90150b8.tar.gz
rust-61f53585aae6f3bdada28a2ddd05b759b90150b8.zip
Rollup merge of #146581 - estebank:issue-146489, r=lcnr
Detect attempt to use var-args in closure

```
error: unexpected `...`
  --> $DIR/no-closure.rs:11:14
   |
LL |     let f = |...| {};
   |              ^^^ not a valid pattern
   |
   = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

error: unexpected `...`
  --> $DIR/no-closure.rs:16:17
   |
LL |     let f = |_: ...| {};
   |                 ^^^
   |
   = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
```

Fix rust-lang/rust#146489, when trying to use c-style var-args in a closure. We emit a more targeted message. We also silence inference errors when the pattern is `PatKind::Err`.
-rw-r--r--compiler/rustc_parse/messages.ftl4
-rw-r--r--compiler/rustc_parse/src/errors.rs12
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs27
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs13
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs13
-rw-r--r--tests/ui/c-variadic/no-closure.rs11
-rw-r--r--tests/ui/c-variadic/no-closure.stderr27
7 files changed, 71 insertions, 36 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 72cd75f6d89..f83cf645f82 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -189,6 +189,10 @@ parse_dotdotdot = unexpected token: `...`
 parse_dotdotdot_rest_pattern = unexpected `...`
     .label = not a valid pattern
     .suggestion = for a rest pattern, use `..` instead of `...`
+    .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+parse_dotdotdot_rest_type = unexpected `...`
+    .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 parse_double_colon_in_bound = expected `:` followed by trait or lifetime
     .suggestion = use single colon
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 00ca5acd84d..1abeee6fe43 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2723,7 +2723,9 @@ pub(crate) struct DotDotDotRestPattern {
     #[label]
     pub span: Span,
     #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")]
-    pub suggestion: Span,
+    pub suggestion: Option<Span>,
+    #[note]
+    pub var_args: Option<()>,
 }
 
 #[derive(Diagnostic)]
@@ -3031,6 +3033,14 @@ pub(crate) struct NestedCVariadicType {
 }
 
 #[derive(Diagnostic)]
+#[diag(parse_dotdotdot_rest_type)]
+#[note]
+pub(crate) struct InvalidCVariadicType {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parse_invalid_dyn_keyword)]
 #[help]
 pub(crate) struct InvalidDynKeyword {
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index c4d30b3d328..fda19d62bc7 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -756,7 +756,7 @@ impl<'a> Parser<'a> {
             self.bump(); // `..`
             PatKind::Rest
         } else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) {
-            self.recover_dotdotdot_rest_pat(lo)
+            self.recover_dotdotdot_rest_pat(lo, expected)
         } else if let Some(form) = self.parse_range_end() {
             self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`.
         } else if self.eat(exp!(Bang)) {
@@ -886,16 +886,27 @@ impl<'a> Parser<'a> {
 
     /// Recover from a typoed `...` pattern that was encountered
     /// Ref: Issue #70388
-    fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind {
+    fn recover_dotdotdot_rest_pat(&mut self, lo: Span, expected: Option<Expected>) -> PatKind {
         // A typoed rest pattern `...`.
         self.bump(); // `...`
 
-        // The user probably mistook `...` for a rest pattern `..`.
-        self.dcx().emit_err(DotDotDotRestPattern {
-            span: lo,
-            suggestion: lo.with_lo(lo.hi() - BytePos(1)),
-        });
-        PatKind::Rest
+        if let Some(Expected::ParameterName) = expected {
+            // We have `...` in a closure argument, likely meant to be var-arg, which aren't
+            // supported in closures (#146489).
+            PatKind::Err(self.dcx().emit_err(DotDotDotRestPattern {
+                span: lo,
+                suggestion: None,
+                var_args: Some(()),
+            }))
+        } else {
+            // The user probably mistook `...` for a rest pattern `..`.
+            self.dcx().emit_err(DotDotDotRestPattern {
+                span: lo,
+                suggestion: Some(lo.with_lo(lo.hi() - BytePos(1))),
+                var_args: None,
+            });
+            PatKind::Rest
+        }
     }
 
     /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 23aaafac934..65347496599 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -15,8 +15,8 @@ use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
 use crate::errors::{
     self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword,
     ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg,
-    HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
-    NestedCVariadicType, ReturnTypesUseThinArrow,
+    HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut,
+    NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow,
 };
 use crate::parser::item::FrontMatterParsingMode;
 use crate::parser::{FnContext, FnParseMode};
@@ -106,6 +106,15 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool {
 impl<'a> Parser<'a> {
     /// Parses a type.
     pub fn parse_ty(&mut self) -> PResult<'a, Box<Ty>> {
+        if self.token == token::DotDotDot {
+            // We special case this so that we don't talk about "nested C-variadics" in types.
+            // We still pass in `AllowCVariadic::No` so that `parse_ty_common` can complain about
+            // things like `Vec<...>`.
+            let span = self.token.span;
+            self.bump();
+            let kind = TyKind::Err(self.dcx().emit_err(InvalidCVariadicType { span }));
+            return Ok(self.mk_ty(span, kind));
+        }
         // Make sure deeply nested types don't overflow the stack.
         ensure_sufficient_stack(|| {
             self.parse_ty_common(
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
index edab530590b..75283dc4ffa 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
@@ -4,11 +4,12 @@ use std::path::PathBuf;
 
 use rustc_errors::codes::*;
 use rustc_errors::{Diag, IntoDiagArg};
-use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource};
+use rustc_hir::{
+    self as hir, Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, PatKind,
+};
 use rustc_middle::bug;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
@@ -512,7 +513,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     type_name: ty_to_string(self, ty, def_id),
                 });
             }
-            InferSourceKind::ClosureArg { insert_span, ty } => {
+            InferSourceKind::ClosureArg { insert_span, ty, .. } => {
                 infer_subdiags.push(SourceKindSubdiag::LetLike {
                     span: insert_span,
                     name: String::new(),
@@ -652,6 +653,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }),
         };
         *err.long_ty_path() = long_ty_path;
+        if let InferSourceKind::ClosureArg { kind: PatKind::Err(_), .. } = kind {
+            // We will have already emitted an error about this pattern.
+            err.downgrade_to_delayed_bug();
+        }
         err
     }
 }
@@ -673,6 +678,7 @@ enum InferSourceKind<'tcx> {
     ClosureArg {
         insert_span: Span,
         ty: Ty<'tcx>,
+        kind: PatKind<'tcx>,
     },
     GenericArg {
         insert_span: Span,
@@ -1197,6 +1203,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
                     kind: InferSourceKind::ClosureArg {
                         insert_span: param.pat.span.shrink_to_hi(),
                         ty: param_ty,
+                        kind: param.pat.kind,
                     },
                 })
             }
diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs
index c0b77786e8b..830ed962a8c 100644
--- a/tests/ui/c-variadic/no-closure.rs
+++ b/tests/ui/c-variadic/no-closure.rs
@@ -4,14 +4,17 @@
 // Check that `...` in closures is rejected.
 
 const F: extern "C" fn(...) = |_: ...| {};
-//~^ ERROR C-variadic type `...` may not be nested inside another type
+//~^ ERROR: unexpected `...`
+//~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 fn foo() {
     let f = |...| {};
-    //~^ ERROR: `..` patterns are not allowed here
-    //~| ERROR: unexpected `...`
+    //~^ ERROR: unexpected `...`
+    //~| NOTE: not a valid pattern
+    //~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
     let f = |_: ...| {};
-    //~^ ERROR C-variadic type `...` may not be nested inside another type
+    //~^ ERROR: unexpected `...`
+    //~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
     f(1i64)
 }
diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr
index 77bd106bb95..0946c4632e6 100644
--- a/tests/ui/c-variadic/no-closure.stderr
+++ b/tests/ui/c-variadic/no-closure.stderr
@@ -1,35 +1,26 @@
-error[E0743]: C-variadic type `...` may not be nested inside another type
+error: unexpected `...`
   --> $DIR/no-closure.rs:6:35
    |
 LL | const F: extern "C" fn(...) = |_: ...| {};
    |                                   ^^^
+   |
+   = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 error: unexpected `...`
-  --> $DIR/no-closure.rs:10:14
+  --> $DIR/no-closure.rs:11:14
    |
 LL |     let f = |...| {};
    |              ^^^ not a valid pattern
    |
-help: for a rest pattern, use `..` instead of `...`
-   |
-LL -     let f = |...| {};
-LL +     let f = |..| {};
-   |
+   = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error[E0743]: C-variadic type `...` may not be nested inside another type
-  --> $DIR/no-closure.rs:14:17
+error: unexpected `...`
+  --> $DIR/no-closure.rs:16:17
    |
 LL |     let f = |_: ...| {};
    |                 ^^^
-
-error: `..` patterns are not allowed here
-  --> $DIR/no-closure.rs:10:14
-   |
-LL |     let f = |...| {};
-   |              ^^^
    |
-   = note: only allowed in tuple, tuple struct, and slice patterns
+   = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0743`.