2025-10-08 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/
* 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