https://github.com/AaronBallman updated https://github.com/llvm/llvm-project/pull/131166
>From 92e26f7fd99774ea9716389d3108547dbb878f90 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 13 Mar 2025 12:24:18 -0400 Subject: [PATCH 1/6] [C23] Add __builtin_c23_va_start This builtin is supported by GCC and is a way to improve diagnostic behavior for va_start in C23 mode. C23 no longer requires a second argument to the va_start macro in support of variadic functions with no leading parameters. However, we still want to diagnose passing more than two arguments, or diagnose when passing something other than the last parameter in the variadic function. This also updates the freestanding <stdarg.h> header to use the new builtin, same as how GCC works. --- clang/docs/ReleaseNotes.rst | 4 +++ clang/include/clang/Basic/Builtins.h | 1 + clang/include/clang/Basic/Builtins.td | 6 ++++ clang/include/clang/Sema/Sema.h | 6 ++-- clang/lib/Basic/Builtins.cpp | 3 ++ clang/lib/Headers/__stdarg_va_arg.h | 4 +-- clang/lib/Sema/SemaChecking.cpp | 41 ++++++++++++++++++------- clang/test/C/C23/n2975.c | 21 +++---------- clang/test/Sema/c23-varargs.c | 43 +++++++++++++++++++++++++++ 9 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 clang/test/Sema/c23-varargs.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e16cfc69590fa..abb87c16c4ddb 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -123,6 +123,10 @@ C2y Feature Support C23 Feature Support ^^^^^^^^^^^^^^^^^^^ +- Added ``__builtin_c23_va_start()`` for compatibility with GCC and to enable + better diagnostic behavior for the ``va_start()`` macro in C23 and later. + This also updates the definition of ``va_start()`` in ``<stdarg.h>`` to use + the new builtin. Fixes #GH124031. Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h index 12c250afb4c61..3a5e31de2bc50 100644 --- a/clang/include/clang/Basic/Builtins.h +++ b/clang/include/clang/Basic/Builtins.h @@ -44,6 +44,7 @@ enum LanguageID : uint16_t { OCL_DSE = 0x400, // builtin requires OpenCL device side enqueue. ALL_OCL_LANGUAGES = 0x800, // builtin for OCL languages. HLSL_LANG = 0x1000, // builtin requires HLSL. + C23_LANG = 0x2000, // builtin requires C23 or later. ALL_LANGUAGES = C_LANG | CXX_LANG | OBJC_LANG, // builtin for all languages. ALL_GNU_LANGUAGES = ALL_LANGUAGES | GNU_LANG, // builtin requires GNU mode. ALL_MS_LANGUAGES = ALL_LANGUAGES | MS_LANG // builtin requires MS mode. diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 2fbdfaea57ccd..526f7040aebea 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -833,6 +833,12 @@ def BuiltinVaStart : Builtin { let Prototype = "void(__builtin_va_list_ref, ...)"; } +def BuiltinC32VaStart : LangBuiltin<"C23_LANG"> { + let Spellings = ["__builtin_c23_va_start"]; + let Attributes = [NoThrow, CustomTypeChecking]; + let Prototype = "void(__builtin_va_list_ref, ...)"; +} + def BuiltinStdargStart : Builtin { let Spellings = ["__builtin_stdarg_start"]; let Attributes = [NoThrow, CustomTypeChecking]; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9ac26d8728446..026eb2b0e93ef 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2499,9 +2499,9 @@ class Sema final : public SemaBase { void checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, CallExpr *TheCall); - /// Check the arguments to '__builtin_va_start' or '__builtin_ms_va_start' - /// for validity. Emit an error and return true on failure; return false - /// on success. + /// Check the arguments to '__builtin_va_start', '__builtin_ms_va_start', + /// or '__builtin_c23_va_start' for validity. Emit an error and return true + /// on failure; return false on success. bool BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall); bool BuiltinVAStartARMMicrosoft(CallExpr *Call); diff --git a/clang/lib/Basic/Builtins.cpp b/clang/lib/Basic/Builtins.cpp index e7829a461bbc5..885abdc152e3a 100644 --- a/clang/lib/Basic/Builtins.cpp +++ b/clang/lib/Basic/Builtins.cpp @@ -191,6 +191,9 @@ static bool builtinIsSupported(const llvm::StringTable &Strings, /* consteval Unsupported */ if (!LangOpts.CPlusPlus20 && strchr(AttributesStr.data(), 'G') != nullptr) return false; + /* C23 unsupported */ + if (!LangOpts.C23 && BuiltinInfo.Langs == C23_LANG) + return false; return true; } diff --git a/clang/lib/Headers/__stdarg_va_arg.h b/clang/lib/Headers/__stdarg_va_arg.h index 89bd2f65d3bea..ebdb6f9d4b1eb 100644 --- a/clang/lib/Headers/__stdarg_va_arg.h +++ b/clang/lib/Headers/__stdarg_va_arg.h @@ -10,8 +10,8 @@ #ifndef va_arg #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L -/* C23 does not require the second parameter for va_start. */ -#define va_start(ap, ...) __builtin_va_start(ap, 0) +/* C23 uses a special builtin. */ +#define va_start(...) __builtin_c23_va_start(__VA_ARGS__) #else /* Versions before C23 do require the second parameter. */ #define va_start(ap, param) __builtin_va_start(ap, param) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 9bcb014ab9bfc..91cdcdf6bb5ac 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2161,6 +2161,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BI__builtin_ms_va_start: case Builtin::BI__builtin_stdarg_start: case Builtin::BI__builtin_va_start: + case Builtin::BI__builtin_c23_va_start: if (BuiltinVAStart(BuiltinID, TheCall)) return ExprError(); break; @@ -4844,15 +4845,27 @@ static bool checkVAStartIsInVariadicFunction(Sema &S, Expr *Fn, bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { Expr *Fn = TheCall->getCallee(); - if (checkVAStartABI(*this, BuiltinID, Fn)) return true; - // In C23 mode, va_start only needs one argument. However, the builtin still - // requires two arguments (which matches the behavior of the GCC builtin), - // <stdarg.h> passes `0` as the second argument in C23 mode. - if (checkArgCount(TheCall, 2)) - return true; + if (BuiltinID == Builtin::BI__builtin_c23_va_start) { + // This builtin requires one argument (the va_list), allows two arguments, + // but diagnoses more than two arguments. e.g., + // __builtin_c23_va_start(); // error + // __builtin_c23_va_start(list); // ok + // __builtin_c23_va_start(list, param); // ok + // __builtin_c23_va_start(list, anything, anything); // error + // This differs from the GCC behavior in that they accept the last case + // with a warning, but it doesn't seem like a useful behavior to allow. + if (checkArgCountRange(TheCall, 1, 2)) + return true; + } else { + // In C23 mode, va_start only needs one argument. However, the builtin still + // requires two arguments (which matches the behavior of the GCC builtin), + // <stdarg.h> passes `0` as the second argument in C23 mode. + if (checkArgCount(TheCall, 2)) + return true; + } // Type-check the first argument normally. if (checkBuiltinArgument(*this, TheCall, 0)) @@ -4864,14 +4877,22 @@ bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { return true; // Verify that the second argument to the builtin is the last argument of the - // current function or method. In C23 mode, if the second argument is an - // integer constant expression with value 0, then we don't bother with this - // check. + // current function or method. In C23 mode and the call is not to + // __builtin_c23_va_start, if the second argument is an integer constant + // expression with value 0, then we don't bother with this check. For + // __builtin_c23_va_start, we only perform the check for the second argument + // being the last argument to the current function if there is a second + // argument present. + if (BuiltinID == Builtin::BI__builtin_c23_va_start && + TheCall->getNumArgs() < 2) + return false; + bool SecondArgIsLastNamedArgument = false; const Expr *Arg = TheCall->getArg(1)->IgnoreParenCasts(); if (std::optional<llvm::APSInt> Val = TheCall->getArg(1)->getIntegerConstantExpr(Context); - Val && LangOpts.C23 && *Val == 0) + Val && LangOpts.C23 && *Val == 0 && + BuiltinID != Builtin::BI__builtin_c23_va_start) return false; // These are valid if SecondArgIsLastNamedArgument is false after the next diff --git a/clang/test/C/C23/n2975.c b/clang/test/C/C23/n2975.c index 2269400fe47c3..5c3225e9fd876 100644 --- a/clang/test/C/C23/n2975.c +++ b/clang/test/C/C23/n2975.c @@ -6,27 +6,20 @@ #include <stdarg.h> -#define DERP this is an error - void func(...) { // expected-warning {{'...' as the only parameter of a function is incompatible with C standards before C23}} // Show that va_start doesn't require the second argument in C23 mode. va_list list; - va_start(list); // expected-warning {{passing no argument for the '...' parameter of a variadic macro is incompatible with C standards before C23}} expected-note@* {{macro 'va_start' defined here}} - va_end(list); - - // Show that va_start doesn't expand or evaluate the second argument. - va_start(list, DERP); + va_start(list); va_end(list); - // FIXME: it would be kinder to diagnose this instead of silently accepting it. - va_start(list, 1, 2); + va_start(list, 1, 2); // expected-error {{too many arguments to function call, expected at most 2, have 3}} va_end(list); // We didn't change the behavior of __builtin_va_start (and neither did GCC). __builtin_va_start(list); // expected-error {{too few arguments to function call, expected 2, have 1}} // Verify that the return type of a call to va_start is 'void'. - _Static_assert(__builtin_types_compatible_p(__typeof__(va_start(list)), void), ""); // expected-warning {{passing no argument for the '...' parameter of a variadic macro is incompatible with C standards before C23}} expected-note@* {{macro 'va_start' defined here}} + _Static_assert(__builtin_types_compatible_p(__typeof__(va_start(list)), void), ""); _Static_assert(__builtin_types_compatible_p(__typeof__(__builtin_va_start(list, 0)), void), ""); } @@ -37,13 +30,7 @@ typedef void (*fp)(...); // expected-warning {{'...' as the only parameter of a // Passing something other than the argument before the ... is still not valid. void diag(int a, int b, ...) { va_list list; - // FIXME: the call to va_start should also diagnose the same way as the call - // to __builtin_va_start. However, because va_start is not allowed to expand - // or evaluate the second argument, we can't pass it along to - // __builtin_va_start to get that diagnostic. So in C17 and earlier, we will - // diagnose this use through the macro, but in C23 and later we've lost the - // diagnostic entirely. GCC has the same issue currently. - va_start(list, a); + va_start(list, a); // expected-warning {{second argument to 'va_start' is not the last named parameter}} // However, the builtin itself is under no such constraints regarding // expanding or evaluating the second argument, so it can still diagnose. __builtin_va_start(list, a); // expected-warning {{second argument to 'va_start' is not the last named parameter}} diff --git a/clang/test/Sema/c23-varargs.c b/clang/test/Sema/c23-varargs.c new file mode 100644 index 0000000000000..fa59357843f13 --- /dev/null +++ b/clang/test/Sema/c23-varargs.c @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c23 -fsyntax-only -ffreestanding -verify=expected,both %s -triple i386-pc-unknown +// RUN: %clang_cc1 -std=c23 -fsyntax-only -ffreestanding -verify=expected,both %s -triple x86_64-apple-darwin9 +// RUN: %clang_cc1 -std=c23 -fsyntax-only -ffreestanding -fms-compatibility -verify=expected,both %s -triple x86_64-pc-win32 +// RUN: %clang_cc1 -std=c17 -fsyntax-only -ffreestanding -verify=both,pre-c23 %s + +void foo(int x, int y, ...) { + __builtin_va_list list; + __builtin_c23_va_start(); // pre-c23-error {{use of unknown builtin '__builtin_c23_va_start'}} \ + expected-error{{too few arguments to function call, expected 1, have 0}} + // Note, the unknown builtin diagnostic is only issued once per function, + // which is why the rest of the lines do not get the same diagonstic. + __builtin_c23_va_start(list); // ok + __builtin_c23_va_start(list, 0); // expected-warning {{second argument to 'va_start' is not the last named parameter}} + __builtin_c23_va_start(list, x); // expected-warning {{second argument to 'va_start' is not the last named parameter}} + __builtin_c23_va_start(list, y); // ok + __builtin_c23_va_start(list, 0, 1); // expected-error {{too many arguments to function call, expected at most 2, have 3}} + __builtin_c23_va_start(list, y, y); // expected-error {{too many arguments to function call, expected at most 2, have 3}} +} + +// Test the same thing as above, only with the macro from stdarg.h. This will +// not have the unknown builtin diagnostics, but will have different +// diagnostics between C23 and earlier modes. +#include <stdarg.h> +void bar(int x, int y, ...) { + // FIXME: the "use of undeclared identifier 'va_start'" diagnostics is an odd + // follow-on diagnostic that should be silenced. + va_list list; + va_start(); // pre-c23-error {{too few arguments provided to function-like macro invocation}} \ + pre-c23-error {{use of undeclared identifier 'va_start'}} \ + expected-error{{too few arguments to function call, expected 1, have 0}} + va_start(list); // pre-c23-error {{too few arguments provided to function-like macro invocation}} \ + pre-c23-error {{use of undeclared identifier 'va_start'}} + va_start(list, 0); // both-warning {{second argument to 'va_start' is not the last named parameter}} + va_start(list, x); // both-warning {{second argument to 'va_start' is not the last named parameter}} + va_start(list, y); // ok + va_start(list, 0, 1); // pre-c23-error {{too many arguments provided to function-like macro invocation}} \ + pre-c23-error {{use of undeclared identifier 'va_start'}} \ + expected-error {{too many arguments to function call, expected at most 2, have 3}} + va_start(list, y, y); // pre-c23-error {{too many arguments provided to function-like macro invocation}} \ + pre-c23-error {{use of undeclared identifier 'va_start'}} \ + expected-error {{too many arguments to function call, expected at most 2, have 3}} + // pre-c23-note@__stdarg_va_arg.h:* 4 {{macro 'va_start' defined here}} +} >From 533db29c3e7a9d898db693942ada41adc424bf6c Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 13 Mar 2025 12:48:55 -0400 Subject: [PATCH 2/6] Add documentation --- clang/docs/LanguageExtensions.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 387251804fa76..cc12ff5bad353 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4283,6 +4283,17 @@ the ellipsis. This function initializes the given ``__builtin_va_list`` object. It is undefined behavior to call this function on an already initialized ``__builtin_va_list`` object. +* ``void __builtin_c23_va_start(__builtin_va_list list, ...)`` + +A builtin function for the target-specific ``va_start`` function-like macro, +available only in C23 and later. The builtin accepts zero or one argument for +the ellipsis (``...``). If such an argument is provided, it should be the name +of the parameter preceeding the ellipsis, which is used for compatibility with +C versions before C23. It is an error to provide two or more variadic arguments. +This function initializes the given ``__builtin_va_list`` object. It is +undefined behavior to call this function on an already initialized +``__builtin_va_list`` object. + * ``void __builtin_va_end(__builtin_va_list list)`` A builtin function for the target-specific ``va_end`` function-like macro. This >From fbcf4d6744332fd8ddb42b7ead9da102e9f38a37 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 13 Mar 2025 13:01:51 -0400 Subject: [PATCH 3/6] Update based on review feedback --- clang/lib/Sema/SemaChecking.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 91cdcdf6bb5ac..19b3b5e61159a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4876,18 +4876,17 @@ bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { if (checkVAStartIsInVariadicFunction(*this, Fn, &LastParam)) return true; - // Verify that the second argument to the builtin is the last argument of the - // current function or method. In C23 mode and the call is not to - // __builtin_c23_va_start, if the second argument is an integer constant - // expression with value 0, then we don't bother with this check. For - // __builtin_c23_va_start, we only perform the check for the second argument - // being the last argument to the current function if there is a second - // argument present. + // Verify that the second argument to the builtin is the last non-variadic + // argument of the current function or method. In C23 mode and the call is + // not to __builtin_c23_va_start, if the second argument is an integer + // constant expression with value 0, then we don't bother with this check. + // For __builtin_c23_va_start, we only perform the check for the second + // argument being the last argument to the current function if there is a + // second argument present. if (BuiltinID == Builtin::BI__builtin_c23_va_start && TheCall->getNumArgs() < 2) return false; - bool SecondArgIsLastNamedArgument = false; const Expr *Arg = TheCall->getArg(1)->IgnoreParenCasts(); if (std::optional<llvm::APSInt> Val = TheCall->getArg(1)->getIntegerConstantExpr(Context); @@ -4900,7 +4899,7 @@ bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { QualType Type; SourceLocation ParamLoc; bool IsCRegister = false; - + bool SecondArgIsLastNamedArgument = false; if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Arg)) { if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(DR->getDecl())) { SecondArgIsLastNamedArgument = PV == LastParam; >From 0d958dbc3f9a37054b69da49b0ec481ee26fc441 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 13 Mar 2025 14:42:02 -0400 Subject: [PATCH 4/6] Fixes based on review feedback --- clang/include/clang/Basic/Builtins.td | 2 +- clang/lib/Sema/SemaChecking.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 526f7040aebea..72a5e495c4059 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -833,7 +833,7 @@ def BuiltinVaStart : Builtin { let Prototype = "void(__builtin_va_list_ref, ...)"; } -def BuiltinC32VaStart : LangBuiltin<"C23_LANG"> { +def BuiltinC23VaStart : LangBuiltin<"C23_LANG"> { let Spellings = ["__builtin_c23_va_start"]; let Attributes = [NoThrow, CustomTypeChecking]; let Prototype = "void(__builtin_va_list_ref, ...)"; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 19b3b5e61159a..139a05bad389c 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4877,8 +4877,8 @@ bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { return true; // Verify that the second argument to the builtin is the last non-variadic - // argument of the current function or method. In C23 mode and the call is - // not to __builtin_c23_va_start, if the second argument is an integer + // argument of the current function or method. In C23 mode, if the call is + // not to __builtin_c23_va_start, and the second argument is an integer // constant expression with value 0, then we don't bother with this check. // For __builtin_c23_va_start, we only perform the check for the second // argument being the last argument to the current function if there is a >From 55c5dbcfbc176dddec363920d62fef15f21219a7 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Fri, 14 Mar 2025 08:44:56 -0400 Subject: [PATCH 5/6] Same great builtin, but now with more codegen! --- clang/lib/CodeGen/CGBuiltin.cpp | 1 + clang/test/CodeGen/varargs.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index a5ed2595bad4d..c126f88b9e3a5 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3551,6 +3551,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_stdarg_start: case Builtin::BI__builtin_va_start: case Builtin::BI__va_start: + case Builtin::BI__builtin_c23_va_start: case Builtin::BI__builtin_va_end: EmitVAStartEnd(BuiltinID == Builtin::BI__va_start ? EmitScalarExpr(E->getArg(0)) diff --git a/clang/test/CodeGen/varargs.c b/clang/test/CodeGen/varargs.c index 625399b87f7ad..71ca88c0d635d 100644 --- a/clang/test/CodeGen/varargs.c +++ b/clang/test/CodeGen/varargs.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -std=c2y -emit-llvm -o - %s | FileCheck %s // PR6433 - Don't crash on va_arg(typedef). typedef double gdouble; @@ -21,3 +21,14 @@ void vla(int n, ...) void *p; p = __builtin_va_arg(ap, typeof (int (*)[++n])); // CHECK: add nsw i32 {{.*}}, 1 } + +// Ensure that __builtin_va_start(list, 0) and __builtin_c23_va_start(list) +// have the same codegen. +void noargs(...) { + __builtin_va_list list; + // CHECK: %list = alloca ptr + __builtin_va_start(list, 0); + // CHECK-NEXT: call void @llvm.va_start.p0(ptr %list) + __builtin_c23_va_start(list); + // CHECK-NEXT: call void @llvm.va_start.p0(ptr %list) +} >From 7a25c552a399a5722ccc80633d304e9018b393b7 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Fri, 14 Mar 2025 09:47:21 -0400 Subject: [PATCH 6/6] Add pre-C23 compatibility diagnostic --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ clang/lib/Sema/SemaChecking.cpp | 8 ++++++-- clang/test/C/C23/n2975.c | 6 +++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8e6e6e892cdd7..04e4af619cd87 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10619,6 +10619,9 @@ def warn_second_arg_of_va_start_not_last_named_param : Warning< def warn_c17_compat_ellipsis_only_parameter : Warning< "'...' as the only parameter of a function is incompatible with C standards " "before C23">, DefaultIgnore, InGroup<CPre23Compat>; +def warn_c17_compat_va_start_one_arg : Warning< + "passing only one argument to 'va_start' is incompatible with C standards " + "before C23">, DefaultIgnore, InGroup<CPre23Compat>; def warn_va_start_type_is_undefined : Warning< "passing %select{an object that undergoes default argument promotion|" "an object of reference type|a parameter declared with the 'register' " diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 139a05bad389c..8fc1dcd56fb2c 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4884,15 +4884,19 @@ bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) { // argument being the last argument to the current function if there is a // second argument present. if (BuiltinID == Builtin::BI__builtin_c23_va_start && - TheCall->getNumArgs() < 2) + TheCall->getNumArgs() < 2) { + Diag(TheCall->getExprLoc(), diag::warn_c17_compat_va_start_one_arg); return false; + } const Expr *Arg = TheCall->getArg(1)->IgnoreParenCasts(); if (std::optional<llvm::APSInt> Val = TheCall->getArg(1)->getIntegerConstantExpr(Context); Val && LangOpts.C23 && *Val == 0 && - BuiltinID != Builtin::BI__builtin_c23_va_start) + BuiltinID != Builtin::BI__builtin_c23_va_start) { + Diag(TheCall->getExprLoc(), diag::warn_c17_compat_va_start_one_arg); return false; + } // These are valid if SecondArgIsLastNamedArgument is false after the next // block. diff --git a/clang/test/C/C23/n2975.c b/clang/test/C/C23/n2975.c index 5c3225e9fd876..797fbd867d9c4 100644 --- a/clang/test/C/C23/n2975.c +++ b/clang/test/C/C23/n2975.c @@ -9,7 +9,7 @@ void func(...) { // expected-warning {{'...' as the only parameter of a function is incompatible with C standards before C23}} // Show that va_start doesn't require the second argument in C23 mode. va_list list; - va_start(list); + va_start(list); // expected-warning {{passing only one argument to 'va_start' is incompatible with C standards before C23}} va_end(list); va_start(list, 1, 2); // expected-error {{too many arguments to function call, expected at most 2, have 3}} @@ -19,8 +19,8 @@ void func(...) { // expected-warning {{'...' as the only parameter of a function __builtin_va_start(list); // expected-error {{too few arguments to function call, expected 2, have 1}} // Verify that the return type of a call to va_start is 'void'. - _Static_assert(__builtin_types_compatible_p(__typeof__(va_start(list)), void), ""); - _Static_assert(__builtin_types_compatible_p(__typeof__(__builtin_va_start(list, 0)), void), ""); + _Static_assert(__builtin_types_compatible_p(__typeof__(va_start(list)), void), ""); // expected-warning {{passing only one argument to 'va_start' is incompatible with C standards before C23}} + _Static_assert(__builtin_types_compatible_p(__typeof__(__builtin_va_start(list, 0)), void), ""); // expected-warning {{passing only one argument to 'va_start' is incompatible with C standards before C23}} } // Show that function pointer types also don't need an argument before the _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits