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
{ "__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 },
#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
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:
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);
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)
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)
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));
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)));
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));
}
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)
}
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);
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)
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;
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));
(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
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);
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:
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);
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
= 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);
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;
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
}
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_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));
/* 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));
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;
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)
#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
{
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);
}
#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
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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 ();
+}
--- /dev/null
+// 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 }
--- /dev/null
+// 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 }
--- /dev/null
+// 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"
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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" }
+}
--- /dev/null
+// 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 ();
+}
--- /dev/null
+// 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
void foo(float a) {
int (*xx)(...);
- xx = isnan;
+ xx = (int (*)(...)) isnan;
if (xx(a))
g++;
}