Hi!

The C++26 https://wg21.link/P3348R4 C++26 should refer to C23 not C17
paper among other things changes va_start macro in the similar way
how C23 has changed it.  Now, unlike C17 and older, C++ has since forever
allowed int (...) but just one wasn't able to use va_start/va_arg/va_end
in such functions.
With the current C++26 draft wording, we'd have to
  #define va_start(V, ...) __builtin_va_start (V, 0)
like we've used for C23 before the PR107980 change.
But Jonathan has kindly filed
https://cplusplus.github.io/LWG/issue4388
which similarly to C23 will if accepted allow to define it as
  #define va_start(...) __builtin_c23_va_start(__VA_ARGS__)
and let the compiler diagnose undesirable cases (see stdarg6.C
testcase in the patch for what it can diagnose, basically anything
that isn't either va_start (ap) or va_start (ap, i) where i is the
last argument's identifier).  This patch implements what assumes
LWG4388 will pass.

Oh, I forgot to enable
  #define __STDC_VERSION_STDARG_H__ 202311L
also for C++26, shall I do this in this patch or incrementally?

Anyway, the hardest part is actually something different.
C23 had to differentiate between C99 void foo (); i.e. unspecified
arguments (but not stdarg) and the new C23 void bar (...); which
is stdarg, but in both cases TYPE_ARG_TYPES (fntype) is NULL.
This has been implemented through the new TYPE_NO_NAMED_ARGS_STDARG_P
flag, fntypes with that flag set are considered stdarg_p and allow
va_start in those, while fntypes with NULL TYPE_ARG_TYPES but the
flag cleared are not stdarg_p, can accept any number of arguments
but can't use va_start.
So, I had to change various places in the C++ FE to pass true
as the third argument to build_function_type for calls which are
meant to be (...) so that one can actually use va_start in those.

Could do it only for C++26 if you want, the patch does it for all
std modes for now.

Anyway, first bootstrapped/regtested the patch without the
(def_fn_type, c_common_nodes_and_builtins) hunks in c-common.cc and
without the g++.dg/opt/pr60849.C test change.

This resulted in
+FAIL: g++.dg/opt/pr60849.C  -std=gnu++?? (internal compiler error: canonical 
types differ for identical types 'int(...)' and 'int(...)')
+FAIL: g++.dg/opt/pr60849.C  -std=gnu++?? (test for excess errors)
+UNRESOLVED: c-c++-common/torture/harden-cfr-bret*.c   -O*   
scan-tree-dump-times hardcfr "__builtin_trap" 1
+UNRESOLVED: c-c++-common/torture/harden-cfr-bret*.c   -O*   
scan-tree-dump-times hardcfr "__hardcfr_check" 2
+FAIL: c-c++-common/torture/harden-cfr-bret*.c   -O*  (internal compiler error: 
canonical types differ for identical types 'void(...)' and 'void(...)')
+FAIL: c-c++-common/torture/harden-cfr-bret*.c   -O*  (test for excess errors)
regressions (many different harden-cfr-bret*.c tests and -O* combinations
and all -std modes of pr60849.C.
The problem is that c-family/c-common.cc uses TYPE_ARG_TYPES NULL
FUNCTION_TYPEs for kind of unspecified arguments even for C++ and
also for C23 (even when it doesn't have them).  Both for some
builtins (e.g. __builtin_signbit (...) or __builtin_isnan (...)
or other type-generic builtins or even signbit (...)/isnan (...))
and for #pragma weak.  The ICEs are because structural_comptypes
doesn't compare TYPE_NO_NAMED_ARGS_STDARG_P flag yet TYPE_CANONICAL
for those types are different because that flag is compared by
the middle-end for type hashing etc.

Next I've tried the two extra hunks in c-common.cc and
tweaking pr60849.C as in the patch.  This instead has
+FAIL: c-c++-common/dfp/signbit-2.c  -std=gnu++?? (test for excess errors)
+UNRESOLVED: c-c++-common/dfp/signbit-2.c  -std=gnu++?? compilation failed to 
produce executable
+FAIL: g++.dg/ext/weak1.C  -std=gnu++?? (test for excess errors)
+FAIL: g++.dg/other/isnan.C  -std=c++?? (test for excess errors)
regressions.

So, I think we need to decide what we want for the builtins
and #pragma weak.  One possibility is treat them like before,
non-stdarg functions with unspecified arguments (i.e. kind of C99
in C++), change structural_comptypes to also compare
TYPE_NO_NAMED_ARGS_STDARG_P flag (to make the ICE go away), but
having permerrors when somebody like harden-cfr* or pr60849.C
tries to cast those builtin/#pragma weak unspecified non-stdarg arguments
to (...) arguments.  I think this is a better option, though perhaps
we could error when somebody tries to do decltype (__builtin_signbit)
or something similar to avoid those magic unspecified argument types
leaking into normal C++ code.

Or don't have any unspecified argument functions, not even builtins
and require them to be (...) but then live with
those 3 tests not working.
One is trying to declare
extern "C" int signbit (double);
which is kind of wrong because signbit is a builtin type-generic
macro and no signbit function exists, another is doing the same
for isnan:
extern "C" int isnan (double);
(I think we could live with these 2), but weak1.C is
#pragma weak bar1 = foo1
extern "C" void foo1 (void) {}
where #pragma weak kind of introduces a (...) type.

As for builtins, this is about
DEF_EXT_LIB_BUILTIN    (BUILT_IN_SIGNBIT, "signbit", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW, "add_overflow", BT_FN_BOOL_VAR, 
ATTR_NOTHROW_NONNULL_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW, "sub_overflow", BT_FN_BOOL_VAR, 
ATTR_NOTHROW_NONNULL_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW, "mul_overflow", BT_FN_BOOL_VAR, 
ATTR_NOTHROW_NONNULL_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW_P, "add_overflow_p", 
BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW_P, "sub_overflow_p", 
BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW_P, "mul_overflow_p", 
BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_AGGREGATE_INCOMING_ADDRESS, 
"aggregate_incoming_address", BT_FN_PTR_VAR, ATTR_LEAF_LIST)
DEF_GCC_BUILTIN        (BUILT_IN_APPLY_ARGS, "apply_args", BT_FN_PTR_VAR, 
ATTR_LEAF_LIST)
DEF_GCC_BUILTIN        (BUILT_IN_CLASSIFY_TYPE, "classify_type", BT_FN_INT_VAR, 
ATTR_LEAF_LIST)
DEF_GCC_BUILTIN        (BUILT_IN_CLEAR_PADDING, "clear_padding", 
BT_FN_VOID_VAR, ATTR_NOTHROW_NONNULL_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_CLZG, "clzg", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_CONSTANT_P, "constant_p", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN        (BUILT_IN_CTZG, "ctzg", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_CLRSBG, "clrsbg", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_FFSG, "ffsg", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISFINITE, "isfinite", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISINF_SIGN, "isinf_sign", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_C99_C90RES_BUILTIN (BUILT_IN_ISINF, "isinf", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC)
DEF_C99_C90RES_BUILTIN (BUILT_IN_ISNAN, "isnan", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISNORMAL, "isnormal", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISGREATER, "isgreater", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISGREATEREQUAL, "isgreaterequal", 
BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISLESS, "isless", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISLESSEQUAL, "islessequal", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISLESSGREATER, "islessgreater", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISUNORDERED, "isunordered", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISEQSIG, "iseqsig", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_ISSIGNALING, "issignaling", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_NEXT_ARG, "next_arg", BT_FN_PTR_VAR, 
ATTR_LEAF_LIST)
DEF_GCC_BUILTIN        (BUILT_IN_PARITYG, "parityg", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_POPCOUNTG, "popcountg", BT_FN_INT_VAR, 
ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN        (BUILT_IN_SAVEREGS, "saveregs", BT_FN_PTR_VAR, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_SPECULATION_SAFE_VALUE_N, "speculation_safe_value",
                 BT_FN_VOID_VAR, ATTR_NOVOPS_NOTHROW_LEAF_LIST)
plus quite a few non-_N suffixed __sync and __atomic builtins.
Only signbit, isinf and isnan are in the non-__builtin_/__sync_/__atomic_
namespace.

Thoughts on this?

2025-10-04  Jakub Jelinek  <[email protected]>

gcc/
        * ginclude/stdarg.h (va_start): Use __builtin_c23_va_start
        also for C++26.
gcc/c-family/
        * c-common.h (D_CXX26): Define.
        * c-common.cc (c_common_resword): Add D_CXX26 to
        __builtin_c23_va_start flags, mention D_CXX26 in comment.
        (def_fn_type, c_common_nodes_and_builtins): For C++ pass
        true as third argument to build_function_type with NULL second
        argument, for C keep passing false.
gcc/cp/
        * lex.cc: Implement va_start changes from P3348R4 - C++26 should
        refer to C23 not C17 paper.
        (init_reswords): Set D_CXX26 in mask for C++23 and older.
        * parser.cc (cp_parser_primary_expression): Handle RID_C23_VA_START.
        * cp-objcp-common.cc (names_builtin_p): Likewise.
        * decl.cc (grokdeclarator, check_function_type, static_fn_type):
        Pass true as last build_function_type argument if in some cases
        second argument is NULL or if original FUNCTION_TYPE has
        TYPE_NO_NAMED_ARGS_STDARG_P flag set.
        * typeck.cc (merge_types): Likewise.
        * lambda.cc (maybe_add_lambda_conv_op): Likewise.
        * tree.cc (strip_typedefs): Likewise.
        * name-lookup.cc (push_local_extern_decl_alias): Likewise.
        * module.cc (trees_in::tree_node): Likewise.
        * pt.cc (copy_default_args_to_explicit_spec,
        rebuild_function_or_method_type, build_deduction_guide,
        alias_ctad_tweaks): Likewise.
        * decl2.cc (change_return_type, cp_reconstruct_complex_type):
        Likewise.
gcc/testsuite/
        * c-c++-common/cpp/has-builtin-4.c: Expect
        __has_builtin (__builtin_c23_va_start) == 1 also for C++26.
        * c-c++-common/Wvarargs.c (foo3): Don't expect undefined behavior
        warning for C++26.
        * g++.dg/cpp26/stdarg1.C: New test.
        * g++.dg/cpp26/stdarg2.C: New test.
        * g++.dg/cpp26/stdarg3.C: New test.
        * g++.dg/cpp26/stdarg4.C: New test.
        * g++.dg/cpp26/stdarg5.C: New test.
        * g++.dg/cpp26/stdarg6.C: New test.
        * g++.dg/cpp26/stdarg7.C: New test.
        * g++.dg/cpp26/stdarg8.C: New test.
        * g++.dg/opt/pr60849.C: Add -fpermissive and
        -Wno-builtin-declaration-mismatch to dg-options.  Expect one
        warning.

--- gcc/ginclude/stdarg.h.jj    2025-04-08 14:08:51.778274529 +0200
+++ gcc/ginclude/stdarg.h       2025-10-03 10:51:31.236489034 +0200
@@ -44,7 +44,8 @@ typedef __builtin_va_list __gnuc_va_list
    if this invocation was from the user program.  */
 #ifdef _STDARG_H
 
-#if defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L) \
+    || __cplusplus + 0 >= 202400L
 #define va_start(...) __builtin_c23_va_start(__VA_ARGS__)
 #else
 #define va_start(v,l)  __builtin_va_start(v,l)
--- gcc/c-family/c-common.h.jj  2025-08-23 15:00:03.686795640 +0200
+++ gcc/c-family/c-common.h     2025-10-03 10:54:25.343045652 +0200
@@ -448,6 +448,7 @@ extern machine_mode c_default_pointer_mo
 #define D_CXX20                0x8000  /* In C++, C++20 only.  */
 #define D_CXX_COROUTINES 0x10000  /* In C++, only with coroutines.  */
 #define D_CXX_MODULES  0x20000  /* In C++, only with modules.  */
+#define D_CXX26         0x40000        /* In C++, C++26 only.  */
 
 #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
 #define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
--- gcc/c-family/c-common.cc.jj 2025-07-27 23:31:09.053015646 +0200
+++ gcc/c-family/c-common.cc    2025-10-03 21:23:18.602399379 +0200
@@ -376,9 +376,10 @@ static bool nonnull_check_p (tree, unsig
    C --std=c17: D_C23 | D_CXXONLY | D_OBJC
    C --std=c23: D_CXXONLY | D_OBJC
    ObjC is like C except that D_OBJC and D_CXX_OBJC are not set
-   C++ --std=c++98: D_CONLY | D_CXX11 | D_CXX20 | D_OBJC
-   C++ --std=c++11: D_CONLY | D_CXX20 | D_OBJC
-   C++ --std=c++20: D_CONLY | D_OBJC
+   C++ --std=c++98: D_CONLY | D_CXX11 | D_CXX20 | D_CXX26 | D_OBJC
+   C++ --std=c++11: D_CONLY | D_CXX20 | D_CXX26 | D_OBJC
+   C++ --std=c++20: D_CONLY | D_CXX26 | D_OBJC
+   C++ --std=c++26: D_CONLY | D_OBJC
    ObjC++ is like C++ except that D_OBJC is not set
 
    If -fno-asm is used, D_ASM is added to the mask.  If
@@ -462,7 +463,7 @@ const struct c_common_resword c_common_r
   { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
   { "__builtin_offsetof", RID_OFFSETOF, 0 },
   { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
-  { "__builtin_c23_va_start", RID_C23_VA_START,        D_C23 },
+  { "__builtin_c23_va_start", RID_C23_VA_START,        D_C23 | D_CXX26 },
   { "__builtin_va_arg",        RID_VA_ARG,     0 },
   { "__complex",       RID_COMPLEX,    0 },
   { "__complex__",     RID_COMPLEX,    0 },
@@ -4249,7 +4250,7 @@ def_fn_type (builtin_type def, builtin_t
     goto egress;
   if (var)
     if (n == 0)
-      t = build_function_type (t, NULL_TREE);
+      t = build_function_type (t, NULL_TREE, c_dialect_cxx ());
     else
       t = build_varargs_function_type_array (t, n, args);
   else
@@ -4823,7 +4824,8 @@ c_common_nodes_and_builtins (void)
     uintptr_type_node =
       TREE_TYPE (identifier_global_value (c_get_ident (UINTPTR_TYPE)));
 
-  default_function_type = build_function_type (integer_type_node, NULL_TREE);
+  default_function_type
+    = build_function_type (integer_type_node, NULL_TREE, c_dialect_cxx ());
   unsigned_ptrdiff_type_node = c_common_unsigned_type (ptrdiff_type_node);
 
   lang_hooks.decls.pushdecl
--- gcc/cp/lex.cc.jj    2025-08-15 22:31:17.263081477 +0200
+++ gcc/cp/lex.cc       2025-10-03 10:56:22.929395459 +0200
@@ -243,6 +243,8 @@ init_reswords (void)
     mask |= D_CXX11;
   if (cxx_dialect < cxx20)
     mask |= D_CXX20;
+  if (cxx_dialect < cxx26)
+    mask |= D_CXX26;
   if (!flag_concepts)
     mask |= D_CXX_CONCEPTS;
   if (!flag_coroutines)
--- gcc/cp/parser.cc.jj 2025-09-29 15:01:08.929841143 +0200
+++ gcc/cp/parser.cc    2025-10-03 13:41:55.940833808 +0200
@@ -6407,6 +6407,110 @@ cp_parser_primary_expression (cp_parser
            return build_x_va_arg (combined_loc, expression, type);
          }
 
+       case RID_C23_VA_START:
+         {
+           location_t start_loc
+             = cp_lexer_peek_token (parser->lexer)->location;
+           cp_lexer_consume_token (parser->lexer);
+           /* Look for the opening `('.  */
+           matching_parens parens;
+           parens.require_open (parser);
+           location_t arg_loc
+             = cp_lexer_peek_token (parser->lexer)->location;
+           /* Now, parse the assignment-expression.  */
+           tree expression = cp_parser_assignment_expression (parser);
+           if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+             {
+               location_t cloc
+                 = cp_lexer_peek_token (parser->lexer)->location;
+               if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+                 {
+                   cp_parser_skip_to_closing_parenthesis (parser, false,
+                                                          false,
+                                                          /*consume_paren=*/
+                                                          true);
+                   return error_mark_node;
+                 }
+               if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)
+                   && cp_lexer_nth_token_is (parser->lexer, 2,
+                                             CPP_CLOSE_PAREN))
+                 {
+                   tree name = cp_lexer_peek_token (parser->lexer)->u.value;
+                   location_t nloc
+                     = cp_lexer_peek_token (parser->lexer)->location;
+                   tree decl = lookup_name (name);
+                   tree last_parm
+                     = tree_last (DECL_ARGUMENTS (current_function_decl));
+                   if (!last_parm || decl != last_parm)
+                     warning_at (nloc, OPT_Wvarargs,
+                                 "optional second parameter of %<va_start%> "
+                                 "not last named argument");
+                   cp_lexer_consume_token (parser->lexer);
+                 }
+               else
+                 {
+                   unsigned nesting_depth = 0;
+                   location_t sloc
+                     = cp_lexer_peek_token (parser->lexer)->location;
+                   location_t eloc = sloc;
+
+                   /* For va_start (ap,) the ) comes from stdarg.h.
+                      Use location of , in that case, otherwise without
+                      -Wsystem-headers nothing is reported.  After all,
+                      the problematic token is the comma in that case.  */
+                   if (cp_lexer_next_token_is (parser->lexer,
+                                               CPP_CLOSE_PAREN))
+                     sloc = eloc = cloc;
+                   while (true)
+                     {
+                       cp_token *token = cp_lexer_peek_token (parser->lexer);
+                       if (token->type == CPP_CLOSE_PAREN && !nesting_depth)
+                         break;
+
+                       if (token->type == CPP_EOF)
+                         break;
+                       if (token->type == CPP_OPEN_PAREN)
+                         ++nesting_depth;
+                       else if (token->type == CPP_CLOSE_PAREN)
+                         --nesting_depth;
+                       else if (token->type == CPP_PRAGMA)
+                         {
+                           cp_parser_skip_to_pragma_eol (parser, token);
+                           continue;
+                         }
+                       eloc = token->location;
+                       cp_lexer_consume_token (parser->lexer);
+                     }
+                   if (sloc != eloc)
+                     sloc = make_location (sloc, sloc, eloc);
+                   warning_at (sloc, OPT_Wvarargs,
+                               "%<va_start%> macro used with additional "
+                               "arguments other than identifier of the "
+                               "last named argument");
+                 }
+             }
+           /* Look for the closing `)'.  */
+           location_t finish_loc
+             = cp_lexer_peek_token (parser->lexer)->location;
+           /* Construct a location of the form:
+                __builtin_c23_va_start (ap, arg)
+                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
+              with the caret at the first argument, ranging from the start
+              of the "__builtin_c23_va_start" token to the close paren.  */
+           location_t combined_loc
+             = make_location (arg_loc, start_loc, finish_loc);
+           parens.require_close (parser);
+           tree fndecl = builtin_decl_explicit (BUILT_IN_VA_START);
+           releasing_vec args;
+           vec_safe_push (args, expression);
+           vec_safe_push (args, integer_zero_node);
+           tree ret = finish_call_expr (fndecl, &args, false, true,
+                                        tf_warning_or_error);
+           if (TREE_CODE (ret) == CALL_EXPR)
+             SET_EXPR_LOCATION (ret, combined_loc);
+           return ret;
+         }
+
        case RID_OFFSETOF:
          return cp_parser_builtin_offsetof (parser);
 
--- gcc/cp/cp-objcp-common.cc.jj        2025-08-13 22:10:18.875791893 +0200
+++ gcc/cp/cp-objcp-common.cc   2025-10-03 13:34:54.311712860 +0200
@@ -588,6 +588,7 @@ names_builtin_p (const char *name)
     case RID_BUILTIN_BIT_CAST:
     case RID_OFFSETOF:
     case RID_VA_ARG:
+    case RID_C23_VA_START:
       return 1;
     case RID_BUILTIN_OPERATOR_NEW:
     case RID_BUILTIN_OPERATOR_DELETE:
--- gcc/cp/decl.cc.jj   2025-09-29 15:01:29.500558100 +0200
+++ gcc/cp/decl.cc      2025-10-03 12:35:51.273320939 +0200
@@ -14874,7 +14874,8 @@ grokdeclarator (const cp_declarator *dec
                is_xobj_member_function = false;
              }
 
-           type = build_function_type (type, arg_types);
+           type = build_function_type (type, arg_types,
+                                       arg_types == NULL_TREE);
 
            tree attrs = declarator->std_attributes;
            if (tx_qual)
@@ -18862,7 +18863,8 @@ check_function_type (tree decl, tree cur
                                             void_type_node,
                                             TREE_CHAIN (args));
       else
-       fntype = build_function_type (void_type_node, args);
+       fntype = build_function_type (void_type_node, args,
+                                     TYPE_NO_NAMED_ARGS_STDARG_P (fntype));
       fntype = (cp_build_type_attribute_variant
                (fntype, TYPE_ATTRIBUTES (TREE_TYPE (decl))));
       fntype = cxx_copy_lang_qualifiers (fntype, TREE_TYPE (decl));
@@ -20356,7 +20358,8 @@ static_fn_type (tree memfntype)
     return memfntype;
   gcc_assert (TREE_CODE (memfntype) == METHOD_TYPE);
   args = TYPE_ARG_TYPES (memfntype);
-  fntype = build_function_type (TREE_TYPE (memfntype), TREE_CHAIN (args));
+  fntype = build_function_type (TREE_TYPE (memfntype), TREE_CHAIN (args),
+                               TREE_CHAIN (args) == NULL_TREE);
   fntype = apply_memfn_quals (fntype, type_memfn_quals (memfntype));
   fntype = (cp_build_type_attribute_variant
            (fntype, TYPE_ATTRIBUTES (memfntype)));
--- gcc/cp/typeck.cc.jj 2025-09-29 15:01:29.000000000 +0200
+++ gcc/cp/typeck.cc    2025-10-03 13:00:55.107299019 +0200
@@ -1035,7 +1035,7 @@ merge_types (tree t1, tree t2)
        gcc_assert (quals == type_memfn_quals (t2));
        gcc_assert (rqual == type_memfn_rqual (t2));
 
-       tree rval = build_function_type (valtype, parms);
+       tree rval = build_function_type (valtype, parms, parms == NULL_TREE);
        rval = apply_memfn_quals (rval, quals);
        tree raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1),
                                                  TYPE_RAISES_EXCEPTIONS (t2));
@@ -1059,9 +1059,13 @@ merge_types (tree t1, tree t2)
           original type of type member function (i.e., without
           the class instance variable up front.  */
        t1 = build_function_type (TREE_TYPE (t1),
-                                 TREE_CHAIN (TYPE_ARG_TYPES (t1)));
+                                 TREE_CHAIN (TYPE_ARG_TYPES (t1)),
+                                 TREE_CHAIN (TYPE_ARG_TYPES (t1))
+                                 == NULL_TREE);
        t2 = build_function_type (TREE_TYPE (t2),
-                                 TREE_CHAIN (TYPE_ARG_TYPES (t2)));
+                                 TREE_CHAIN (TYPE_ARG_TYPES (t2)),
+                                 TREE_CHAIN (TYPE_ARG_TYPES (t2))
+                                 == NULL_TREE);
        t3 = merge_types (t1, t2);
        t3 = build_method_type_directly (basetype, TREE_TYPE (t3),
                                         TYPE_ARG_TYPES (t3));
--- gcc/cp/lambda.cc.jj 2025-09-23 10:26:42.616783697 +0200
+++ gcc/cp/lambda.cc    2025-10-03 12:41:13.378810817 +0200
@@ -1329,9 +1329,10 @@ maybe_add_lambda_conv_op (tree type)
     }
 
   tree stattype
-    = build_function_type (fn_result, FUNCTION_FIRST_USER_PARMTYPE (callop));
-  stattype = (cp_build_type_attribute_variant
-             (stattype, TYPE_ATTRIBUTES (optype)));
+    = build_function_type (fn_result, FUNCTION_FIRST_USER_PARMTYPE (callop),
+                          FUNCTION_FIRST_USER_PARMTYPE (callop) == NULL_TREE);
+  stattype = cp_build_type_attribute_variant (stattype,
+                                             TYPE_ATTRIBUTES (optype));
   if (flag_noexcept_type
       && TYPE_NOTHROW_P (TREE_TYPE (callop)))
     stattype = build_exception_variant (stattype, noexcept_true_spec);
--- gcc/cp/tree.cc.jj   2025-09-29 15:01:09.007840070 +0200
+++ gcc/cp/tree.cc      2025-10-03 12:58:53.553993724 +0200
@@ -1782,7 +1782,8 @@ strip_typedefs (tree t, bool *remove_att
          }
        else
          {
-           result = build_function_type (type, arg_types);
+           result = build_function_type (type, arg_types,
+                                         TYPE_NO_NAMED_ARGS_STDARG_P (t));
            result = apply_memfn_quals (result, type_memfn_quals (t));
          }
 
--- gcc/cp/name-lookup.cc.jj    2025-09-15 10:28:58.094365150 +0200
+++ gcc/cp/name-lookup.cc       2025-10-03 12:51:59.944773095 +0200
@@ -3700,7 +3700,11 @@ push_local_extern_decl_alias (tree decl)
                          chain = &TREE_CHAIN (*chain);
                        }
 
-                   tree fn_type = build_function_type (TREE_TYPE (type), 
nargs);
+                   bool no_named_args_stdarg
+                     = TYPE_NO_NAMED_ARGS_STDARG_P (type);
+                   tree fn_type
+                     = build_function_type (TREE_TYPE (type), nargs,
+                                            no_named_args_stdarg);
 
                    fn_type = apply_memfn_quals
                      (fn_type, type_memfn_quals (type));
--- gcc/cp/module.cc.jj 2025-09-29 15:01:29.000000000 +0200
+++ gcc/cp/module.cc    2025-10-03 12:49:06.462198042 +0200
@@ -10397,7 +10397,7 @@ trees_in::tree_node (bool is_use)
                  if (klass)
                    res = build_method_type_directly (klass, res, args);
                  else
-                   res = build_function_type (res, args);
+                   res = build_function_type (res, args, args == NULL_TREE);
                }
            }
            break;
--- gcc/cp/pt.cc.jj     2025-09-29 15:01:29.522557798 +0200
+++ gcc/cp/pt.cc        2025-10-03 12:55:17.647009610 +0200
@@ -2655,7 +2655,8 @@ copy_default_args_to_explicit_spec (tree
     }
   else
     new_type = build_function_type (TREE_TYPE (old_type),
-                                   new_spec_types);
+                                   new_spec_types,
+                                   new_spec_types == NULL_TREE);
   new_type = cp_build_type_attribute_variant (new_type,
                                              TYPE_ATTRIBUTES (old_type));
   new_type = cxx_copy_lang_qualifiers (new_type, old_type);
@@ -14776,7 +14777,8 @@ rebuild_function_or_method_type (tree t,
   tree new_type;
   if (TREE_CODE (t) == FUNCTION_TYPE)
     {
-      new_type = build_function_type (return_type, arg_types);
+      new_type = build_function_type (return_type, arg_types,
+                                     arg_types == NULL_TREE);
       new_type = apply_memfn_quals (new_type, type_memfn_quals (t));
     }
   else
@@ -31097,7 +31099,7 @@ build_deduction_guide (tree type, tree c
        = copy_node (INNERMOST_TEMPLATE_PARMS (tparms));
     }
 
-  tree fntype = build_function_type (type, fparms);
+  tree fntype = build_function_type (type, fparms, fparms == NULL_TREE);
   tree ded_fn = build_lang_decl_loc (loc,
                                     FUNCTION_DECL,
                                     dguide_name (type), fntype);
@@ -31529,7 +31531,8 @@ alias_ctad_tweaks (tree tmpl, tree uguid
          tree fntype = TREE_TYPE (fprime);
          ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
                                       in_decl, NULL_TREE, complain);
-         fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
+         fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype),
+                                       TYPE_NO_NAMED_ARGS_STDARG_P (fntype));
          TREE_TYPE (fprime) = fntype;
          if (TREE_CODE (fprime) == TEMPLATE_DECL)
            TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
--- gcc/cp/decl2.cc.jj  2025-09-29 15:01:29.503558059 +0200
+++ gcc/cp/decl2.cc     2025-10-03 12:33:15.265505686 +0200
@@ -233,7 +233,8 @@ change_return_type (tree new_ret, tree f
 
   if (TREE_CODE (fntype) == FUNCTION_TYPE)
     {
-      newtype = build_function_type (new_ret, args);
+      newtype = build_function_type (new_ret, args,
+                                    TYPE_NO_NAMED_ARGS_STDARG_P (fntype));
       newtype = apply_memfn_quals (newtype,
                                   type_memfn_quals (fntype));
     }
@@ -1698,7 +1699,8 @@ cp_reconstruct_complex_type (tree type,
   else if (TREE_CODE (type) == FUNCTION_TYPE)
     {
       inner = cp_reconstruct_complex_type (TREE_TYPE (type), bottom);
-      outer = build_function_type (inner, TYPE_ARG_TYPES (type));
+      outer = build_function_type (inner, TYPE_ARG_TYPES (type),
+                                  TYPE_NO_NAMED_ARGS_STDARG_P (type));
       outer = apply_memfn_quals (outer, type_memfn_quals (type));
     }
   else if (TREE_CODE (type) == METHOD_TYPE)
--- gcc/testsuite/c-c++-common/cpp/has-builtin-4.c.jj   2025-04-08 
14:09:00.468153554 +0200
+++ gcc/testsuite/c-c++-common/cpp/has-builtin-4.c      2025-10-03 
13:28:44.396880302 +0200
@@ -9,7 +9,7 @@
 #if __has_builtin (__builtin_va_arg) != 1
 #error "no __builtin_va_arg"
 #endif
-#if __STDC_VERSION__ >= 202311L
+#if (__STDC_VERSION__ >= 202311L || __cplusplus >= 202400L)
 #if __has_builtin (__builtin_c23_va_start) != 1
 #error "no __builtin_c23_va_start"
 #endif
--- gcc/testsuite/c-c++-common/Wvarargs.c.jj    2025-04-08 14:09:00.368154947 
+0200
+++ gcc/testsuite/c-c++-common/Wvarargs.c       2025-10-03 13:45:04.867199479 
+0200
@@ -50,6 +50,6 @@ foo3 (int a, register int b, ...)     // { d
 {
     va_list vp;
     /* 'b' is declared with register storage, so warn.  */
-    va_start (vp, b); /* { dg-warning "undefined behavior" } */
+    va_start (vp, b); /* { dg-warning "undefined behavior" "" { target { c || 
c++23_down } } } */
     va_end (vp);
 }
--- gcc/testsuite/g++.dg/cpp26/stdarg1.C.jj     2025-10-03 13:17:26.255449187 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg1.C        2025-10-03 13:37:45.569324902 
+0200
@@ -0,0 +1,158 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do run { target c++26 } }
+
+#include <stdarg.h>
+
+double
+f (...)
+{
+  va_list ap;
+  va_start (ap);
+  double ret = va_arg (ap, int);
+  ret += va_arg (ap, double);
+  ret += va_arg (ap, int);
+  ret += va_arg (ap, double);
+  va_end (ap);
+  return ret;
+}
+
+void
+g (...)
+{
+  va_list ap;
+  va_start (ap, random ! ignored, ignored ** text);    // { dg-warning 
"'va_start' macro used with additional arguments other than identifier of the 
last named argument" }
+  for (int i = 0; i < 10; i++)
+    if (va_arg (ap, double) != i)
+      __builtin_abort ();
+  va_end (ap);
+}
+
+void
+h1 (int x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+void
+h2 (int x(), ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+void
+h3 (int x[10], ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+void
+h4 (char x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+void
+h5 (float x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+void
+h6 (long x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+struct s { char c[1000]; };
+
+void
+h7 (struct s x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+}
+
+int
+main ()
+{
+  if (f (1, 2.0, 3, 4.0) != 10.0)
+    __builtin_abort ();
+  g (0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  g (0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  h1 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  h2 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  h3 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  h4 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  h5 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  h6 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  h7 (s {}, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+}
--- gcc/testsuite/g++.dg/cpp26/stdarg2.C.jj     2025-10-03 13:18:14.462765326 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg2.C        2025-10-03 13:38:22.024816582 
+0200
@@ -0,0 +1,212 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do run { target c++26 } }
+
+#include <stdarg.h>
+
+struct s { char c[1000]; };
+
+struct s
+f (...)
+{
+  va_list ap;
+  va_start (ap);
+  double r = va_arg (ap, int);
+  r += va_arg (ap, double);
+  r += va_arg (ap, int);
+  r += va_arg (ap, double);
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = r;
+  ret.c[999] = 42;
+  return ret;
+}
+
+struct s
+g (...)
+{
+  va_list ap;
+  va_start (ap, random ! ignored, ignored ** text);    // { dg-warning 
"'va_start' macro used with additional arguments other than identifier of the 
last named argument" }
+  for (int i = 0; i < 10; i++)
+    if (va_arg (ap, double) != i)
+      __builtin_abort ();
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 17;
+  ret.c[999] = 58;
+  return ret;
+}
+
+struct s
+h1 (int x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 32;
+  ret.c[999] = 95;
+  return ret;
+}
+
+struct s
+h2 (int x(), ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 5;
+  ret.c[999] = 125;
+  return ret;
+}
+
+struct s
+h3 (int x[10], ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 8;
+  ret.c[999] = 12;
+  return ret;
+}
+
+struct s
+h4 (char x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 18;
+  ret.c[999] = 28;
+  return ret;
+}
+
+struct s
+h5 (float x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 38;
+  ret.c[999] = 48;
+  return ret;
+}
+
+struct s
+h6 (long x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 58;
+  ret.c[999] = 68;
+  return ret;
+}
+
+struct s
+h7 (struct s x, ...)
+{
+  va_list ap;
+  va_start (ap);
+  for (int i = 0; i < 10; i++)
+    {
+      if (va_arg (ap, double) != i)
+       __builtin_abort ();
+      i++;
+      if (va_arg (ap, int) != i)
+       __builtin_abort ();
+    }
+  va_end (ap);
+  struct s ret = {};
+  ret.c[0] = 78;
+  ret.c[999] = 88;
+  return ret;
+}
+
+int
+main ()
+{
+  struct s x = f (1, 2.0, 3, 4.0);
+  if (x.c[0] != 10 || x.c[999] != 42)
+    __builtin_abort ();
+  x = g (0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  if (x.c[0] != 17 || x.c[999] != 58)
+    __builtin_abort ();
+  x = g (0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  if (x.c[0] != 17 || x.c[999] != 58)
+    __builtin_abort ();
+  x = h1 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 32 || x.c[999] != 95)
+    __builtin_abort ();
+  x = h2 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 5 || x.c[999] != 125)
+    __builtin_abort ();
+  x = h3 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 8 || x.c[999] != 12)
+    __builtin_abort ();
+  x = h4 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 18 || x.c[999] != 28)
+    __builtin_abort ();
+  x = h5 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 38 || x.c[999] != 48)
+    __builtin_abort ();
+  x = h6 (0, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 58 || x.c[999] != 68)
+    __builtin_abort ();
+  x = h7 (s {}, 0.0, 1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9);
+  if (x.c[0] != 78 || x.c[999] != 88)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp26/stdarg3.C.jj     2025-10-03 13:21:10.995261065 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg3.C        2025-10-03 13:21:49.338717129 
+0200
@@ -0,0 +1,7 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-O2" }
+
+#include "stdarg1.C"
+
+// { dg-warning "'va_start' macro used with additional arguments other than 
identifier of the last named argument" "" { target *-*-* } 0 }
--- gcc/testsuite/g++.dg/cpp26/stdarg4.C.jj     2025-10-03 13:21:57.829596679 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg4.C        2025-10-03 13:22:02.555529637 
+0200
@@ -0,0 +1,7 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-O2" }
+
+#include "stdarg2.C"
+
+// { dg-warning "'va_start' macro used with additional arguments other than 
identifier of the last named argument" "" { target *-*-* } 0 }
--- gcc/testsuite/g++.dg/cpp26/stdarg5.C.jj     2025-10-03 13:22:44.166939350 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg5.C        2025-10-03 13:23:03.665662735 
+0200
@@ -0,0 +1,5 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-O2" }
+
+#include "../../gcc.dg/c23-stdarg-9.c"
--- gcc/testsuite/g++.dg/cpp26/stdarg6.C.jj     2025-10-03 13:23:25.697350201 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg6.C        2025-10-03 13:39:27.188907954 
+0200
@@ -0,0 +1,112 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-O2" }
+
+#include <stdarg.h>
+
+int i;
+
+void
+f0 (...)
+{
+  va_list ap;
+  va_start (ap);
+  va_end (ap);
+}
+
+void
+f1 (...)
+{
+  va_list ap;
+  va_start (ap, i);                            // { dg-warning "optional 
second parameter of 'va_start' not last named argument" }
+  va_end (ap);
+}
+
+void
+f2 (...)
+{
+  int j = 0;
+  va_list ap;
+  va_start (ap, j);                            // { dg-warning "optional 
second parameter of 'va_start' not last named argument" }
+  va_end (ap);
+}
+
+void
+f3 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, k);                            // { dg-warning "optional 
second parameter of 'va_start' not last named argument" }
+  va_end (ap);
+}
+
+void
+f4 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, l);
+  va_end (ap);
+}
+
+void
+f5 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, (int) l);                      // { dg-warning "'va_start' 
macro used with additional arguments other than identifier of the last named 
argument" }
+  va_end (ap);
+}
+
+void
+f6 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, l + 0);                                // { dg-warning 
"'va_start' macro used with additional arguments other than identifier of the 
last named argument" }
+  va_end (ap);
+}
+
+void
+f7 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, ()()(), [][][], {}{}{}, *+-/1({[_*_]})%&&!?!?);        // { 
dg-warning "'va_start' macro used with additional arguments other than 
identifier of the last named argument" }
+  va_end (ap);
+}
+
+void
+f8 (...)
+{
+  va_list ap;
+  va_start (ap,);                              // { dg-warning "'va_start' 
macro used with additional arguments other than identifier of the last named 
argument" }
+  va_end (ap);
+}
+
+void
+f9 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, k+l+****2);                    // { dg-warning "'va_start' 
macro used with additional arguments other than identifier of the last named 
argument" }
+  va_end (ap);
+}
+
+void
+f10 (int m, ...)
+{
+  va_list ap;
+  va_start (ap, m);
+  va_end (ap);
+}
+
+void
+f11 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, ()()()[[[}}});                 // { dg-warning "'va_start' 
macro used with additional arguments other than identifier of the last named 
argument" }
+  va_end (ap);
+}
+
+void
+f12 (int k, int l, ...)
+{
+  va_list ap;
+  va_start (ap, ]]]]]]{{{{{{);                 // { dg-warning "'va_start' 
macro used with additional arguments other than identifier of the last named 
argument" }
+  va_end (ap);
+}
--- gcc/testsuite/g++.dg/cpp26/stdarg7.C.jj     2025-10-03 13:24:38.622317084 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg7.C        2025-10-03 13:43:01.196923899 
+0200
@@ -0,0 +1,11 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-O2" }
+
+#include <stdarg.h>
+
+void
+f (...)
+{
+  va_start (); // { dg-error "expected primary-expression before '\\\)' token" 
}
+}
--- gcc/testsuite/g++.dg/cpp26/stdarg8.C.jj     2025-10-03 13:26:40.556612019 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg8.C        2025-10-03 13:43:28.238546843 
+0200
@@ -0,0 +1,26 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do run { target c++26 } }
+
+#include <stdarg.h>
+
+int
+main ()
+{
+  int v = 0;
+  auto a = [&] (...) {
+    va_list ap;
+    va_start (ap);
+    int b = 42;
+    if (v)
+      b = va_arg (ap, int);
+    va_end (ap);
+    return b;
+  };
+  if (a () != 42)
+    __builtin_abort ();
+  v = 1;
+  if (a (1, 2) != 1)
+    __builtin_abort ();
+  if (a (13, 2.0f, 2ULL) != 13)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/opt/pr60849.C.jj       2020-01-12 11:54:37.209401399 
+0100
+++ gcc/testsuite/g++.dg/opt/pr60849.C  2025-10-03 21:42:17.184156710 +0200
@@ -1,5 +1,5 @@
 // { dg-do compile }
-// { dg-options "-O2" }
+// { dg-options "-O2 -fpermissive -Wno-builtin-declaration-mismatch" }
 
 int g;
 
@@ -7,7 +7,7 @@ extern "C" int isnan ();
 
 void foo(float a) {
   int (*xx)(...);
-  xx = isnan;
+  xx = isnan;  // { dg-warning "invalid conversion" }
   if (xx(a))
     g++;
 }

        Jakub

Reply via email to