Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ parse_dotdotdot = unexpected token: `...`
parse_dotdotdot_rest_pattern = unexpected `...`
.label = not a valid pattern
.suggestion = for a rest pattern, use `..` instead of `...`
.note = C-variadic type `...` is not allowed here

parse_double_colon_in_bound = expected `:` followed by trait or lifetime
.suggestion = use single colon
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
27 changes: 19 additions & 8 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::path::PathBuf;

use rustc_errors::codes::*;
use rustc_errors::{Diag, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::{self as hir, PatKind};
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
Expand Down Expand Up @@ -512,7 +512,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(),
Expand Down Expand Up @@ -652,6 +652,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();
Comment on lines +657 to +658
Copy link
Contributor

@lcnr lcnr Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, surprised we don't have a way to silence a lint by passing in an ErrorGuaranteed. We could then do

        if let InferSourceKind::ClosureArg { kind: PatKind::Err(guar), .. } = kind {
            err.silence_via_error_guaranteed(guar);
        }
}

though maybe even better... change this function to return Result<Diag, ErrorGuaranteed> and avoid creating a diagnostic at all edit: seems annoying to do 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the rationale for downgrade_to_delayed_bug itself not taking an ErrorGuarantee is because itself is kind of an error guarantee already :)

The guarantee is more critical for anything that could cause a binary to be emitted otherwise.

}
err
}
}
Expand All @@ -673,6 +677,7 @@ enum InferSourceKind<'tcx> {
ClosureArg {
insert_span: Span,
ty: Ty<'tcx>,
kind: PatKind<'tcx>,
},
GenericArg {
insert_span: Span,
Expand Down Expand Up @@ -1197,6 +1202,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,
},
})
}
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/c-variadic/no-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ const F: extern "C" fn(...) = |_: ...| {};

fn foo() {
let f = |...| {};
//~^ ERROR: `..` patterns are not allowed here
//~| ERROR: unexpected `...`
//~^ ERROR: unexpected `...`

let f = |_: ...| {};
//~^ ERROR C-variadic type `...` may not be nested inside another type
Expand Down
18 changes: 3 additions & 15 deletions tests/ui/c-variadic/no-closure.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,14 @@ error: unexpected `...`
LL | let f = |...| {};
| ^^^ not a valid pattern
|
help: for a rest pattern, use `..` instead of `...`
|
LL - let f = |...| {};
LL + let f = |..| {};
|
= note: C-variadic type `...` is not allowed here

error[E0743]: C-variadic type `...` may not be nested inside another type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existing, but what does "may not be nested inside another type" mean here 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just emit the error like using ... in normal fn:

error: `...` is not supported for non-extern functions
 --> src/lib.rs:3:8
  |
3 | fn foo(_: ...) {}
  |        ^^^^^^
  |
  = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

Copy link
Contributor Author

@estebank estebank Sep 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason this error triggers is because it is emitted by parse_ty_common, which was likely meant to catch uses like Vec<...>, but we didn't bother special casing the top-level case. It'll be hard to handle that in parse_ty_common because that doesn't know whether it is a top-level type being parsed, so it would have to be specially handled elsewhere. I'll look into it as a follow up.

Edit: scratch that. Curiosity got the best of me and fixed it quickly.

--> $DIR/no-closure.rs:14:17
--> $DIR/no-closure.rs:13: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

error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0743`.
10 changes: 10 additions & 0 deletions tests/ui/closures/varargs-in-closure-isnt-supported.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// var-args are not supported in closures, ensure we don't misdirect people (#146489)
#![feature(c_variadic)]

unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 {
let mut lol = |...| (); //~ ERROR: unexpected `...`
unsafe { ap.arg::<u32>() } //~^ NOTE: C-variadic type `...` is not allowed here
//~| NOTE: not a valid pattern
}

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/closures/varargs-in-closure-isnt-supported.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: unexpected `...`
--> $DIR/varargs-in-closure-isnt-supported.rs:5:20
|
LL | let mut lol = |...| ();
| ^^^ not a valid pattern
|
= note: C-variadic type `...` is not allowed here

error: aborting due to 1 previous error

Loading