On Sun, Oct 05, 2025 at 04:00:16PM +0100, Jason Merrill wrote:
> > Oh, I forgot to enable
> >    #define __STDC_VERSION_STDARG_H__ 202311L
> > also for C++26, shall I do this in this patch or incrementally?
> 
> In this patch seems desirable.

Here is a new version of the patch, which defines this + adds test for that.

> > 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)
> 
> Pedantically, these only seem like errors at link time if in fact no such
> function is provided by the library, and glibc provides a definition of
> isnan(double).  And the isnan test isn't even linked.  A permerror seems
> wrong, it should only be a warning.
> 
> But also I don't see why we can't handle these redeclarations just as before
> after adding the flag; we just need to adjust the prototype_p check in
> decls_match.

I just went with only setting TYPE_NO_NAMED_ARGS_STDARG_P for cxx_dialect >=
cxx26 in order not to affect older standards at all, and made
structural_comptypes punt on TYPE_NO_NAMED_ARGS_STDARG_P differences (to
avoid the ICEs).
I think all those signbit and isnan tests are just weird, C says those are
macros, not functions, and glibc actually doesn't define those as functions.
So it is just the test that try something weird.  With this patch everything
passes.
As for the permerror, I don't think it is wrong, the ... functions have
sometimes a different ABI (e.g. on x86_64 caller needs to initialize %rax
to number of float ... arguments), so implicitly casting from int ()
function type which doesn't have any arguments to int (...) feels
importantly incompatible.  With explicit cast as now done in this version
of the patch it works fine.

> > but weak1.C is
> > #pragma weak bar1 = foo1
> > extern "C" void foo1 (void) {}
> > where #pragma weak kind of introduces a (...) type.
> 
> I would think the name introduced by #pragma weak should have the type of
> the target; by the time we declare the alias in
> maybe_apply_pending_pragma_weaks we have a declaration for the target or
> it's an error.

I agree maybe_apply_pending_pragma_weaks is just weird (but with this
version of the patch it doesn't fail).  It is only for the
#pragma foo = bar
case where foo has not been declared in the whole function and you just want
to make foo be exported as an alias to bar (everything else is handled
elsewhere).
It seems to be totally wrong e.g. for VAR_DECLs or I think for C23 as well
given that it doesn't have implicit function definitions.
      decl = build_decl (UNKNOWN_LOCATION,
                         target ? TREE_CODE (target->decl) : FUNCTION_DECL,
                         alias_id, default_function_type);
I think perhaps we could move the if (!target) check with error + continue
before this build_decl and drop the target ? and : FUNCTION_DECL parts and
use TREE_TYPE (target->decl) instead of default_function_type?

That said, because it doesn't fail with this version of the patch, perhaps
it can be done independently.

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

gcc/
        * ginclude/stdarg.h (va_start): Use __builtin_c23_va_start
        also for C++26.
        (__STDC_VERSION_STDARG_H__): Also define 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.
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 (grokfndecl, grokdeclarator, check_function_type,
        static_fn_type): Pass true as last build_function_type argument if
        in some cases second argument is NULL for C++26 or if original
        FUNCTION_TYPE has TYPE_NO_NAMED_ARGS_STDARG_P flag set.
        * typeck.cc (merge_types): Likewise.
        (structural_comptypes): Return false for TYPE_NO_NAMED_ARGS_STDARG_P
        differences.
        * 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/cpp26/stdarg9.C: New test.
        * g++.dg/opt/pr60849.C (foo): Add explicit cast.

--- gcc/ginclude/stdarg.h.jj    2025-10-04 09:42:23.775001859 +0200
+++ gcc/ginclude/stdarg.h       2025-10-06 16:05:22.066861182 +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)
@@ -125,7 +126,8 @@ typedef __gnuc_va_list va_list;
 
 #endif /* not __svr4__ */
 
-#if defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L) \
+    || __cplusplus + 0 >= 202400L
 #define __STDC_VERSION_STDARG_H__      202311L
 #endif
 
--- gcc/c-family/c-common.h.jj  2025-10-04 09:42:22.902014176 +0200
+++ gcc/c-family/c-common.h     2025-10-06 16:04:23.785675910 +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-10-04 09:42:22.853014867 +0200
+++ gcc/c-family/c-common.cc    2025-10-06 16:04:23.787675882 +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 },
--- gcc/cp/lex.cc.jj    2025-10-04 09:42:23.307008462 +0200
+++ gcc/cp/lex.cc       2025-10-06 16:04:23.787675882 +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-10-04 09:50:00.063564536 +0200
+++ gcc/cp/parser.cc    2025-10-06 17:05:27.170484820 +0200
@@ -6407,6 +6407,118 @@ 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");
+                   else
+                     {
+                       /* __builtin_va_start parsing does mark the argument
+                          as used and read, for -Wunused* purposes mark it
+                          the same.  */
+                       TREE_USED (last_parm) = 1;
+                       mark_exp_read (last_parm);
+                     }
+                   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-10-04 09:42:22.973013174 +0200
+++ gcc/cp/cp-objcp-common.cc   2025-10-06 16:04:23.794675785 +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-10-04 09:50:00.050564719 +0200
+++ gcc/cp/decl.cc      2025-10-06 16:28:17.305638013 +0200
@@ -12413,11 +12413,13 @@ grokfndecl (tree ctype,
       if (!same_type_p (TREE_TYPE (TREE_TYPE (decl)),
                        integer_type_node))
        {
-         tree oldtypeargs = TYPE_ARG_TYPES (TREE_TYPE (decl));
+         tree dtype = TREE_TYPE (decl);
+         tree oldtypeargs = TYPE_ARG_TYPES (dtype);
          tree newtype;
          error_at (declspecs->locations[ds_type_spec],
                    "%<::main%> must return %<int%>");
-         newtype = build_function_type (integer_type_node, oldtypeargs);
+         newtype = build_function_type (integer_type_node, oldtypeargs,
+                                        TYPE_NO_NAMED_ARGS_STDARG_P (dtype));
          TREE_TYPE (decl) = newtype;
        }
       if (warn_main)
@@ -15324,7 +15326,9 @@ grokdeclarator (const cp_declarator *dec
                is_xobj_member_function = false;
              }
 
-           type = build_function_type (type, arg_types);
+           type = build_function_type (type, arg_types,
+                                       cxx_dialect >= cxx26
+                                       && arg_types == NULL_TREE);
 
            tree attrs = declarator->std_attributes;
            if (tx_qual)
@@ -19312,7 +19316,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));
@@ -20809,7 +20814,9 @@ 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),
+                               cxx_dialect >= cxx26
+                               && 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-10-04 09:42:23.718002663 +0200
+++ gcc/cp/typeck.cc    2025-10-06 16:29:10.688891890 +0200
@@ -1035,7 +1035,9 @@ 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,
+                                        cxx_dialect >= cxx26
+                                        && parms == NULL_TREE);
        rval = apply_memfn_quals (rval, quals);
        tree raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1),
                                                  TYPE_RAISES_EXCEPTIONS (t2));
@@ -1059,9 +1061,15 @@ 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)),
+                                 cxx_dialect >= cxx26
+                                 && (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)),
+                                 cxx_dialect >= cxx26
+                                 && (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));
@@ -1550,8 +1558,11 @@ structural_comptypes (tree t1, tree t2,
        return false;
       break;
 
-    case METHOD_TYPE:
     case FUNCTION_TYPE:
+      if (TYPE_NO_NAMED_ARGS_STDARG_P (t1) != TYPE_NO_NAMED_ARGS_STDARG_P (t2))
+       return false;
+      /* FALLTHRU */
+    case METHOD_TYPE:
       /* Exception specs and memfn_rquals were checked above.  */
       if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
        return false;
--- gcc/cp/lambda.cc.jj 2025-10-04 09:42:23.218009717 +0200
+++ gcc/cp/lambda.cc    2025-10-06 16:28:45.944237742 +0200
@@ -1329,9 +1329,12 @@ 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),
+                          cxx_dialect >= cxx26
+                          && (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-10-04 09:50:00.116563788 +0200
+++ gcc/cp/tree.cc      2025-10-06 16:04:23.799675715 +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-10-06 09:38:31.856370271 +0200
+++ gcc/cp/name-lookup.cc       2025-10-06 16:04:23.800675701 +0200
@@ -3726,7 +3726,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-10-04 09:42:48.922647073 +0200
+++ gcc/cp/module.cc    2025-10-06 16:28:51.549159400 +0200
@@ -10414,7 +10414,9 @@ 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,
+                                              cxx_dialect >= cxx26
+                                              && args == NULL_TREE);
                }
            }
            break;
--- gcc/cp/pt.cc.jj     2025-10-04 09:42:48.957646580 +0200
+++ gcc/cp/pt.cc        2025-10-06 16:29:01.685017737 +0200
@@ -2655,7 +2655,9 @@ copy_default_args_to_explicit_spec (tree
     }
   else
     new_type = build_function_type (TREE_TYPE (old_type),
-                                   new_spec_types);
+                                   new_spec_types,
+                                   cxx_dialect >= cxx26
+                                   && 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);
@@ -14779,7 +14781,9 @@ 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,
+                                     cxx_dialect >= cxx26
+                                     && arg_types == NULL_TREE);
       new_type = apply_memfn_quals (new_type, type_memfn_quals (t));
     }
   else
@@ -31100,7 +31104,9 @@ 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,
+                                    cxx_dialect >= cxx26
+                                    && fparms == NULL_TREE);
   tree ded_fn = build_lang_decl_loc (loc,
                                     FUNCTION_DECL,
                                     dguide_name (type), fntype);
@@ -31532,7 +31538,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-10-04 09:42:23.107011284 +0200
+++ gcc/cp/decl2.cc     2025-10-06 16:04:23.806675617 +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-10-04 
09:42:24.009998543 +0200
+++ gcc/testsuite/c-c++-common/cpp/has-builtin-4.c      2025-10-06 
16:04:23.806675617 +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-10-04 09:42:23.950999376 
+0200
+++ gcc/testsuite/c-c++-common/Wvarargs.c       2025-10-06 16:04:23.806675617 
+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-06 16:04:23.806675617 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg1.C        2025-10-06 16:04:23.806675617 
+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-06 16:04:23.806675617 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg2.C        2025-10-06 16:04:23.806675617 
+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-06 16:04:23.807675603 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg3.C        2025-10-06 16:04:23.807675603 
+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-06 16:04:23.807675603 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg4.C        2025-10-06 16:04:23.807675603 
+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-06 16:04:23.807675603 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg5.C        2025-10-06 16:04:23.807675603 
+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-06 16:04:23.807675603 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg6.C        2025-10-06 16:04:23.807675603 
+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-06 16:04:23.807675603 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg7.C        2025-10-06 16:04:23.807675603 
+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-06 16:04:23.807675603 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg8.C        2025-10-06 16:04:23.807675603 
+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/cpp26/stdarg9.C.jj     2025-10-06 16:37:15.093121882 
+0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg9.C        2025-10-06 16:46:38.552247997 
+0200
@@ -0,0 +1,16 @@
+// P3348R4 - C++26 should refer to C23 not C17
+// { dg-do compile }
+
+#include <stdarg.h>
+
+#if __cplusplus >= 202400L
+#ifndef __STDC_VERSION_STDARG_H__
+#error __STDC_VERSION_STDARG_H__ not defined for C++26
+#elif __STDC_VERSION_STDARG_H__ != 202311L
+#error Unexpected __STDC_VERSION_STDARG_H__ value
+#endif
+#else
+#ifdef __STDC_VERSION_STDARG_H__
+#error __STDC_VERSION_STDARG_H__ defined for C++ < 26
+#endif
+#endif
--- gcc/testsuite/g++.dg/opt/pr60849.C.jj       2025-10-04 09:42:24.164996357 
+0200
+++ gcc/testsuite/g++.dg/opt/pr60849.C  2025-10-06 17:00:45.581416896 +0200
@@ -7,7 +7,7 @@ extern "C" int isnan ();
 
 void foo(float a) {
   int (*xx)(...);
-  xx = isnan;
+  xx = (int (*)(...) isnan;
   if (xx(a))
     g++;
 }

        Jakub

Reply via email to