[PATCH] c++, v3: C++26 va_start - part of P3348R4 - C++26 should refer to C23 not C17

Jakub Jelinek jakub@redhat.com
Wed Oct 8 17:56:09 GMT 2025


On Wed, Oct 08, 2025 at 03:13:31PM +0100, Jason Merrill wrote:
> > --- 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
> 
> Why __cplusplus + 0?  I don't see that pattern anywhere else.

It was the first time I saw it as well, I thought there is some hidden
reason for it, but -Wundef warns about both #if foo >= 202400
and #if foo + 0 >= 202400 equally, so I've dropped it.
Most likely the header is just too old.

It is there in
#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L \
    || __cplusplus + 0 >= 201103L
#define va_copy(d,s)    __builtin_va_copy(d,s)
#endif

> > --- 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:
> 
> Please factor this out into a separate function, with a grammar summary in
> the comment.

Done.

> Why not use cp_parser_skip_to_closing_parenthesis instead of this loop?
> Needs a comment if there's a reason.

Added comment.  With the current wording which matches the C23 wording, e.g.
  va_start (ap, ()()()[[[}}});
and
  va_start (ap, ]]]]]]{{{{{{);
are valid (though we warn about those), all it requires that ()s are
balanced and all preprocessing tokens actually convertible to tokens.

> > +	    location_t combined_loc
> > +	      = make_location (arg_loc, start_loc, finish_loc);
> > +	    parens.require_close (parser);
> 
> require_close returns the token, so it seems unnecessary to peek it first.

require_close returns the token or NULL, so I'd need to deal with what to
do if it returns NULL.

> > @@ -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);
> 
> How about adding cp_build_function_type to encapsulate this pattern?

Done.

2025-10-08  Jakub Jelinek  <jakub@redhat.com>

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/
	* cp-tree.h (cp_build_function_type): Declare.
	* 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_parser_builtin_c23_va_start): New function.
	* cp-objcp-common.cc (names_builtin_p): Likewise.
	* decl.cc (grokfndecl, check_function_type): Pass
	TYPE_NO_NAMED_ARGS_STDARG_P as last arg to build_function_type.
	(grokdeclarator, static_fn_type): Use cp_build_function_type instead
	of build_function_type.
	* typeck.cc (merge_types): Likewise.
	(structural_comptypes): Return false for TYPE_NO_NAMED_ARGS_STDARG_P
	differences.
	* lambda.cc (maybe_add_lambda_conv_op): Use cp_build_function_type
	instead of build_function_type.
	* tree.cc (cp_build_function_type): New function.
	(strip_typedefs): Pass TYPE_NO_NAMED_ARGS_STDARG_P as last arg to
	build_function_type.
	* name-lookup.cc (push_local_extern_decl_alias): Likewise.
	* module.cc (trees_in::tree_node): Use cp_build_function_type instead
	of build_function_type.
	* pt.cc (copy_default_args_to_explicit_spec,
	rebuild_function_or_method_type, build_deduction_guide): Likewise.
	(alias_ctad_tweaks): Pass TYPE_NO_NAMED_ARGS_STDARG_P as last arg to
	build_function_type.
	* 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-08 09:23:48.812694862 +0200
+++ gcc/ginclude/stdarg.h	2025-10-08 18:44:28.803856008 +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 >= 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 >= 202400L
 #define __STDC_VERSION_STDARG_H__	202311L
 #endif
 
--- gcc/c-family/c-common.h.jj	2025-10-08 09:23:47.940706893 +0200
+++ gcc/c-family/c-common.h	2025-10-08 18:43:37.988553400 +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-08 09:23:47.891707569 +0200
+++ gcc/c-family/c-common.cc	2025-10-08 18:43:37.990553372 +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/cp-tree.h.jj	2025-10-08 17:46:23.319698090 +0200
+++ gcc/cp/cp-tree.h	2025-10-08 19:36:19.714162019 +0200
@@ -8491,6 +8491,7 @@ extern tree cp_build_reference_type		(tr
 extern tree move				(tree);
 extern tree cp_build_qualified_type		(tree, int,
 						 tsubst_flags_t = tf_warning_or_error);
+extern tree cp_build_function_type		(tree, tree);
 extern bool cv_qualified_p			(const_tree);
 extern tree cv_unqualified			(tree);
 extern special_function_kind special_function_p (const_tree);
--- gcc/cp/lex.cc.jj	2025-10-08 09:23:48.434700078 +0200
+++ gcc/cp/lex.cc	2025-10-08 18:43:37.990553372 +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-08 09:23:48.654697042 +0200
+++ gcc/cp/parser.cc	2025-10-08 19:28:36.930512900 +0200
@@ -2575,6 +2575,7 @@ static cp_expr cp_parser_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static cp_expr cp_parser_constant_expression
   (cp_parser *, int = 0, bool * = NULL, bool = false);
+static cp_expr cp_parser_builtin_c23_va_start (cp_parser *);
 static cp_expr cp_parser_builtin_offsetof
   (cp_parser *);
 static cp_expr cp_parser_lambda_expression
@@ -6407,6 +6408,9 @@ cp_parser_primary_expression (cp_parser
 	    return build_x_va_arg (combined_loc, expression, type);
 	  }
 
+	case RID_C23_VA_START:
+	  return cp_parser_builtin_c23_va_start (parser);
+
 	case RID_OFFSETOF:
 	  return cp_parser_builtin_offsetof (parser);
 
@@ -11497,6 +11501,133 @@ cp_parser_constant_expression (cp_parser
   return expression;
 }
 
+/* Parse __builtin_c23_va_start.
+
+   c23-va-start-expression:
+     __builtin_c23_va_start ( assignment-expression )
+     __builtin_c23_va_start ( assignment-expression , identifier )
+     __builtin_c23_va_start ( assignment-expression , tokens[opt] )
+
+   The first form is the expected new C++26 form, the second if
+   identifier is the name of the last parameter before ... is meant
+   for backwards compatibility with C++23 and older.
+   The third form where LWG4388 requires all the preprocessing tokens
+   to be convertible to tokens and it can't contain unbalanced
+   parentheses is parsed with a warning and the tokens are just skipped.
+   This is because C++26 like C23 defines va_start macro as
+   va_start (ap, ...) and says second and later arguments to the macro
+   are discarded, yet we want to diagnose when people use something
+   which wasn't valid before C++26 and is not the single argument
+   va_start either.  */
+
+static cp_expr
+cp_parser_builtin_c23_va_start (cp_parser *parser)
+{
+  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;
+	  /* Not using cp_parser_skip_to_closing_parenthesis here, because
+	     the tokens in second and further arguments don't have to be
+	     fully balanced, only can't contain unbalanced parentheses.
+	     So, va_start (ap, [[[[[[[[[{{{{{{{{{}]);
+	     is valid C++ for which we want to warn,
+	     #define X id); something (
+	     va_start (ap, X);
+	     is IFNDR (not detectable unless the preprocessor special cases
+	     va_start macro).  */
+	  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 cp_expr (ret, combined_loc);
+}	  
+
 /* Parse __builtin_offsetof.
 
    offsetof-expression:
--- gcc/cp/cp-objcp-common.cc.jj	2025-10-08 09:23:48.066705154 +0200
+++ gcc/cp/cp-objcp-common.cc	2025-10-08 18:43:37.997553276 +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-08 09:23:48.125704341 +0200
+++ gcc/cp/decl.cc	2025-10-08 19:33:17.164667187 +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,7 @@ grokdeclarator (const cp_declarator *dec
 		is_xobj_member_function = false;
 	      }
 
-	    type = build_function_type (type, arg_types);
+	    type = cp_build_function_type (type, arg_types);
 
 	    tree attrs = declarator->std_attributes;
 	    if (tx_qual)
@@ -19312,7 +19314,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 +20812,7 @@ 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 = cp_build_function_type (TREE_TYPE (memfntype), TREE_CHAIN (args));
   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-08 09:23:48.739695870 +0200
+++ gcc/cp/typeck.cc	2025-10-08 19:35:25.593904723 +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 = cp_build_function_type (valtype, parms);
 	rval = apply_memfn_quals (rval, quals);
 	tree raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1),
 						  TYPE_RAISES_EXCEPTIONS (t2));
@@ -1058,10 +1058,10 @@ merge_types (tree t1, tree t2)
 	/* If this was a member function type, get back to the
 	   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)));
-	t2 = build_function_type (TREE_TYPE (t2),
-				  TREE_CHAIN (TYPE_ARG_TYPES (t2)));
+	t1 = cp_build_function_type (TREE_TYPE (t1),
+				     TREE_CHAIN (TYPE_ARG_TYPES (t1)));
+	t2 = cp_build_function_type (TREE_TYPE (t2),
+				     TREE_CHAIN (TYPE_ARG_TYPES (t2)));
 	t3 = merge_types (t1, t2);
 	t3 = build_method_type_directly (basetype, TREE_TYPE (t3),
 					 TYPE_ARG_TYPES (t3));
@@ -1550,8 +1550,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-08 09:23:48.364701044 +0200
+++ gcc/cp/lambda.cc	2025-10-08 19:33:52.778178456 +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)));
+    = cp_build_function_type (fn_result,
+			      FUNCTION_FIRST_USER_PARMTYPE (callop));
+  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-08 09:23:48.733695953 +0200
+++ gcc/cp/tree.cc	2025-10-08 19:31:49.907864636 +0200
@@ -1537,6 +1537,19 @@ cp_build_qualified_type (tree type, int
   return result;
 }
 
+/* Return a FUNCTION_TYPE for a function returning VALUE_TYPE
+   with ARG_TYPES arguments.  Wrapper around build_function_type
+   which ensures TYPE_NO_NAMED_ARGS_STDARG_P is set if ARG_TYPES
+   is NULL for C++26.  */
+
+tree
+cp_build_function_type (tree value_type, tree arg_types)
+{
+  return build_function_type (value_type, arg_types,
+			      cxx_dialect >= cxx26
+			      && arg_types == NULL_TREE);
+}
+
 /* Return TYPE with const and volatile removed.  */
 
 tree
@@ -1782,7 +1795,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-08 09:23:48.588697953 +0200
+++ gcc/cp/name-lookup.cc	2025-10-08 18:43:38.003553194 +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-08 09:24:14.666338175 +0200
+++ gcc/cp/module.cc	2025-10-08 19:34:04.217021479 +0200
@@ -10414,7 +10414,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 = cp_build_function_type (res, args);
 		}
 	    }
 	    break;
--- gcc/cp/pt.cc.jj	2025-10-08 17:46:17.919772189 +0200
+++ gcc/cp/pt.cc	2025-10-08 19:34:34.908600297 +0200
@@ -2654,8 +2654,7 @@ copy_default_args_to_explicit_spec (tree
 					     new_spec_types);
     }
   else
-    new_type = build_function_type (TREE_TYPE (old_type),
-				    new_spec_types);
+    new_type = cp_build_function_type (TREE_TYPE (old_type), new_spec_types);
   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 +14778,7 @@ 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 = cp_build_function_type (return_type, arg_types);
       new_type = apply_memfn_quals (new_type, type_memfn_quals (t));
     }
   else
@@ -31100,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 = cp_build_function_type (type, fparms);
   tree ded_fn = build_lang_decl_loc (loc,
 				     FUNCTION_DECL,
 				     dguide_name (type), fntype);
@@ -31532,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-10-08 09:23:48.248702644 +0200
+++ gcc/cp/decl2.cc	2025-10-08 18:43:38.009553111 +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-08 09:23:48.937693138 +0200
+++ gcc/testsuite/c-c++-common/cpp/has-builtin-4.c	2025-10-08 18:43:38.025552892 +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-08 09:23:48.906693566 +0200
+++ gcc/testsuite/c-c++-common/Wvarargs.c	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg1.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg2.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg3.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg4.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg5.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg6.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.037552727 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg7.C	2025-10-08 18:43:38.037552727 +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-08 18:43:38.038552713 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg8.C	2025-10-08 18:43:38.038552713 +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-08 18:43:38.038552713 +0200
+++ gcc/testsuite/g++.dg/cpp26/stdarg9.C	2025-10-08 18:43:38.038552713 +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-08 09:23:48.978692572 +0200
+++ gcc/testsuite/g++.dg/opt/pr60849.C	2025-10-08 18:43:38.047552590 +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



More information about the Libstdc++ mailing list