c++: Implement C++26 P1306R5 - Expansion statements [PR120776]

The following patch implements the C++26 P1306R5 - Expansion statements
paper.
When expansion statements are used outside of templates, the lowering
of the statement uses push_tinst_level_loc and instantiates the body
multiple times, otherwise when the new TEMPLATE_FOR_STMT statement is
being instantiated and !processing_template_decl, it instantiates the
body several times with just local_specialization_stack around each
iteration but with the original args.
Because the lowering of these statements is mostly about instantiation,
I've put the lowering code into pt.cc rather than semantics.cc.
Only destructuring expansion statements currently use in the patch
temporary lifetime extension which matches the proposed resolution of
https://cplusplus.github.io/CWG/issues/3043.html
I'm not sure what will CWG decide about that if there will be some
temporary lifetime extension for enumerating expansion statements and if
yes, under what exact rules (e.g. whether it extends all the temporaries
across one iteration of the body, or only if a reference is initialized
or nothing at all).  And for iterating expansion statements, I think I
don't understand the P2686R4 rules well yet, I think if the
expansion-initializer is used in static constexpr rvalue reference, then
it isn't needed, but not sure if it won't be needed if static would be
dropped (whether
struct S { constexpr S () : s (0) {} constexpr ~S () {} int s; };
struct T { const S &t, &u; };
void foo () { constexpr T t = { S {}, S {} }; use (t.t, t.u); }
is ok under P2686R4; though without constexpr before T I see S::~S () being
called after use, not at the end of the t declaration, so maybe it is
fine also without static).
As per
https://cplusplus.github.io/CWG/issues/3044.html
the patch uses build_int_cst (ptrdiff_type_node, i) to create second
operand of begin + i and doesn't lookup overloaded comma operator (note, I'm
actually not even creating a lambda there, just using TARGET_EXPRs).
I guess my preference would be dropping those 4 static keywords from
[stmt.expand] but the patch does use those for now and it won't be possible
to change that until the rest of P2686R4 is implemented.
As per
https://cplusplus.github.io/CWG/issues/3045.html
it treats sk_template_for like sk_for for the purpose of redeclaration of
vars in the body but doesn't yet reject [[fallthrough]]; in the expansion
stmt body (when not nested in another switch).
I'm not sure if cp_perform_range_for_lookup used in the patch is exactly
what we want for the https://eel.is/c++draft/stmt.expand#3.2
- it does finish_call_expr on the perform_koenig_lookup as well, shouldn't
for the decision whether it is iterating or destructing (i.e. tf_none)
just call perform_koenig_lookup and check if it found some
FUNCTION_DECL/OVERLOAD/TEMPLATE_DECL?
cp_decomp_size in the patch has tsubst_flags_t argument and attempts to be
SFINAE friendly, even when it isn't needed strictly for this patch.
This is with PR96185 __builtin_structured_binding_size implementation in
mind (to follow clang).
The new TEMPLATE_FOR_STMT statement is expected to be lowered to something
that doesn't use the statement at all, I've implemented break/continue
discovery in the body, so all I needed was to punt on TEMPLATE_FOR_STMT
in potential_constant_expression_1 so that we don't try to constant evaluate
it when it is still dependent (and cxx_eval_constant_expression rejects
it without any extra code).
I think only enumerating and iterating expansion statements can have zero
iteration, because for destructuring ones it doesn't use a structured
binding pack and so valid structured binding has to have at least one
iteration.  Though
https://cplusplus.github.io/CWG/issues/3048.html
could change that, this patch currently rejects it though.

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

	PR c++/120776
gcc/c-family/
	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
	__cpp_expansion_statements=202506L for C++26.
gcc/cp/
	* cp-tree.def: Implement C++26 P1306R5 - Expansion statements.
	(TEMPLATE_FOR_STMT): New tree code.
	* cp-tree.h (struct saved_scope): Add expansion_stmt.
	(in_expansion_stmt): Define.
	(TEMPLATE_FOR_DECL, TEMPLATE_FOR_EXPR, TEMPLATE_FOR_BODY,
	TEMPLATE_FOR_SCOPE, TEMPLATE_FOR_INIT_STMT): Define.
	(struct tinst_level): Adjust comment.
	(cp_decomp_size, finish_expansion_stmt, do_pushlevel,
	cp_build_range_for_decls, build_range_temp,
	cp_perform_range_for_lookup, begin_template_for_scope): Declare.
	(finish_range_for_stmt): Remove declaration.
	* cp-objcp-common.cc (cp_common_init_ts): Handle TEMPLATE_FOR_STMT.
	* name-lookup.h (enum scope_kind): Add sk_template_for enumerator.
	(struct cp_binding_level): Enlarge kind bitfield from 4 to 5 bits.
	Adjust comment with remaining space bits.
	* name-lookup.cc (check_local_shadow): Handle sk_template_for like
	sk_for.
	(cp_binding_level_descriptor): Add entry for sk_template_for.
	(begin_scope): Handle sk_template_for.
	* parser.h (IN_EXPANSION_STMT): Define.
	* parser.cc (cp_debug_parser): Print IN_EXPANSION_STMT bit.
	(cp_parser_lambda_expression): Temporarily clear in_expansion_stmt.
	(cp_parser_statement): Handle RID_TEMPLATE followed by RID_FOR for
	C++11.
	(cp_parser_label_for_labeled_statement): Complain about named labels
	inside of expansion stmt body.
	(cp_hide_range_decl): New function.
	(cp_parser_range_for): Use it.  Adjust do_range_for_auto_deduction
	caller.  Remove second template argument from auto_vecs bindings and
	names.
	(build_range_temp): No longer static.
	(do_range_for_auto_deduction): Add expansion_stmt argument.
	(cp_build_range_for_decls): New function.
	(cp_convert_range_for): Use it.  Call cp_perform_range_for_lookup
	rather than cp_parser_perform_range_for_lookup.
	(cp_parser_perform_range_for_lookup): Rename to ...
	(cp_perform_range_for_lookup): ... this.  No longer static.  Add
	complain argument and handle it.
	(cp_parser_range_for_member_function): Rename to ...
	(cp_range_for_member_function): ... this.
	(cp_parser_expansion_statement): New function.
	(cp_parser_jump_statement): Handle IN_EXPANSION_STMT.
	(cp_convert_omp_range_for): Adjust do_range_for_auto_deduction caller.
	Call cp_perform_range_for_lookup rather than
	cp_parser_perform_range_for_lookup.
	* error.cc (print_instantiation_full_context): Handle tldcl being
	TEMPLATE_FOR_STMT.
	(print_instantiation_partial_context_line): Likewise.
	* constexpr.cc (potential_constant_expression_1): Handle
	TEMPLATE_FOR_STMT.
	* decl.cc (poplevel_named_label_1): Use obl instead of bl->level_chain.
	(finish_case_label): Diagnose case labels inside of template for.
	(find_decomp_class_base): Add complain argument, don't diagnose
	anything and just return error_mark_node if tf_none, adjust recursive
	call.
	(cp_decomp_size): New function.
	(cp_finish_decomp): Adjust find_decomp_class_base caller.
	* semantics.cc (do_pushlevel): No longer static.
	(begin_template_for_scope): New function.
	* pt.cc (push_tinst_level_loc): Handle TEMPLATE_FOR_STMT.
	(reopen_tinst_level): Likewise.
	(tsubst_stmt): Handle TEMPLATE_FOR_STMT.
	(struct expansion_stmt_bc): New type.
	(expansion_stmt_find_bc_r, finish_expansion_stmt): New functions.
	* decl2.cc (decl_dependent_p): Return true for current function's decl
	if in_expansion_stmt.
	* call.cc (extend_ref_init_temps): Don't extend_all_temps if
	TREE_STATIC (decl).
	* cxx-pretty-print.cc (cxx_pretty_printer::statement): Handle
	TEMPLATE_FOR_STMT.
gcc/testsuite/
	* g++.dg/cpp1z/decomp64.C: New test.
	* g++.dg/cpp26/expansion-stmt1.C: New test.
	* g++.dg/cpp26/expansion-stmt2.C: New test.
	* g++.dg/cpp26/expansion-stmt3.C: New test.
	* g++.dg/cpp26/expansion-stmt4.C: New test.
	* g++.dg/cpp26/expansion-stmt5.C: New test.
	* g++.dg/cpp26/expansion-stmt6.C: New test.
	* g++.dg/cpp26/expansion-stmt7.C: New test.
	* g++.dg/cpp26/expansion-stmt8.C: New test.
	* g++.dg/cpp26/expansion-stmt9.C: New test.
	* g++.dg/cpp26/expansion-stmt10.C: New test.
	* g++.dg/cpp26/expansion-stmt11.C: New test.
	* g++.dg/cpp26/expansion-stmt12.C: New test.
	* g++.dg/cpp26/expansion-stmt13.C: New test.
	* g++.dg/cpp26/expansion-stmt14.C: New test.
	* g++.dg/cpp26/expansion-stmt15.C: New test.
	* g++.dg/cpp26/expansion-stmt16.C: New test.
	* g++.dg/cpp26/expansion-stmt17.C: New test.
	* g++.dg/cpp26/expansion-stmt18.C: New test.
	* g++.dg/cpp26/expansion-stmt19.C: New test.
	* g++.dg/cpp26/feat-cxx26.C: Add __cpp_expansion_statements
	tests.
This commit is contained in:
Jakub Jelinek 2025-08-13 22:07:27 +02:00 committed by Jakub Jelinek
parent 5fedaa2347
commit 458773ac7b
37 changed files with 2952 additions and 164 deletions

View File

@ -1097,6 +1097,7 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_pp_embed=202502L");
cpp_define (pfile, "__cpp_constexpr_virtual_inheritance=202506L");
cpp_define (pfile, "__cpp_trivial_relocatability=202502L");
cpp_define (pfile, "__cpp_expansion_statements=202506L");
}
if (flag_concepts && cxx_dialect > cxx14)
cpp_define (pfile, "__cpp_concepts=202002L");

View File

@ -15032,7 +15032,10 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups,
/* P2718R0 - in C++23 for-range-initializer, extend all temps. */
if (DECL_NAME (decl) == for_range__identifier
&& flag_range_for_ext_temps)
&& flag_range_for_ext_temps
/* Iterating expansion statement decl is static right now, but that
could change depending on CWG3044 and CWG3043. */
&& !TREE_STATIC (decl))
{
gcc_checking_assert (!cond_guard);
return extend_all_temps (decl, init, cleanups);

View File

@ -12475,6 +12475,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case CO_AWAIT_EXPR:
case CO_YIELD_EXPR:
case CO_RETURN_EXPR:
case TEMPLATE_FOR_STMT:
if (flags & tf_error)
constexpr_error (cp_expr_loc_or_loc (t, input_location), fundef_p,
"%qE is not a constant expression", t);

View File

@ -659,6 +659,7 @@ cp_common_init_ts (void)
MARK_TS_EXP (IF_STMT);
MARK_TS_EXP (OMP_DEPOBJ);
MARK_TS_EXP (RANGE_FOR_STMT);
MARK_TS_EXP (TEMPLATE_FOR_STMT);
MARK_TS_EXP (TRY_BLOCK);
MARK_TS_EXP (USING_STMT);

View File

@ -299,6 +299,11 @@ DEFTREECODE (IF_STMT, "if_stmt", tcc_statement, 4)
templates. */
DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 6)
/* Used to represent an expansion-statement. The operands are
TEMPLATE_FOR_DECL, TEMPLATE_FOR_EXPR, TEMPLATE_FOR_BODY,
TEMPLATE_FOR_SCOPE, and TEMPLATE_FOR_INIT_STMT, respectively. */
DEFTREECODE (TEMPLATE_FOR_STMT, "template_for_stmt", tcc_statement, 5)
/* Used to represent an expression statement. Use `EXPR_STMT_EXPR' to
obtain the expression. */
DEFTREECODE (EXPR_STMT, "expr_stmt", tcc_expression, 1)

View File

@ -1973,6 +1973,8 @@ struct GTY(()) saved_scope {
of consteval if statement. Also set while processing an immediate
invocation. */
BOOL_BITFIELD consteval_if_p : 1;
/* Nonzero if we are parsing the substatement of expansion-statement. */
BOOL_BITFIELD expansion_stmt : 1;
int unevaluated_operand;
int inhibit_evaluation_warnings;
@ -2046,6 +2048,7 @@ extern GTY(()) struct saved_scope *scope_chain;
#define in_discarded_stmt scope_chain->discarded_stmt
#define in_consteval_if_p scope_chain->consteval_if_p
#define in_expansion_stmt scope_chain->expansion_stmt
#define current_ref_temp_count scope_chain->ref_temp_count
@ -5691,6 +5694,19 @@ decl_template_parm_check (const_tree t, const char *f, int l, const char *fn)
#define RANGE_FOR_IVDEP(NODE) TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE))
#define RANGE_FOR_NOVECTOR(NODE) TREE_LANG_FLAG_5 (RANGE_FOR_STMT_CHECK (NODE))
/* TEMPLATE_FOR_STMT accessors. These give access to the declarator,
expression, body, and scope of the statement, respectively. */
#define TEMPLATE_FOR_DECL(NODE) \
TREE_OPERAND (TEMPLATE_FOR_STMT_CHECK (NODE), 0)
#define TEMPLATE_FOR_EXPR(NODE) \
TREE_OPERAND (TEMPLATE_FOR_STMT_CHECK (NODE), 1)
#define TEMPLATE_FOR_BODY(NODE) \
TREE_OPERAND (TEMPLATE_FOR_STMT_CHECK (NODE), 2)
#define TEMPLATE_FOR_SCOPE(NODE) \
TREE_OPERAND (TEMPLATE_FOR_STMT_CHECK (NODE), 3)
#define TEMPLATE_FOR_INIT_STMT(NODE) \
TREE_OPERAND (TEMPLATE_FOR_STMT_CHECK (NODE), 4)
/* STMT_EXPR accessor. */
#define STMT_EXPR_STMT(NODE) TREE_OPERAND (STMT_EXPR_CHECK (NODE), 0)
@ -6802,9 +6818,11 @@ struct GTY((chain_next ("%h.next"))) tinst_level {
/* The original node. TLDCL can be a DECL (for a function or static
data member), a TYPE (for a class), depending on what we were
asked to instantiate, or a TREE_LIST with the template as PURPOSE
and the template args as VALUE, if we are substituting for
overload resolution. In all these cases, TARGS is NULL.
asked to instantiate, a TEMPLATE_FOR_STMT (for instantiation
of expansion stmt body outside of templates) or a TREE_LIST with
the template as PURPOSE and the template args as VALUE, if we are
substituting for overload resolution. In all these cases, TARGS
is NULL.
However, to avoid creating TREE_LIST objects for substitutions if
we can help, we store PURPOSE and VALUE in TLDCL and TARGS,
respectively. So TLDCL stands for TREE_LIST or DECL (the
@ -7278,6 +7296,7 @@ extern void omp_declare_variant_finalize (tree, tree);
struct cp_decomp { tree decl; unsigned int count; };
extern void cp_finish_decl (tree, tree, bool, tree, int, cp_decomp * = nullptr);
extern tree lookup_decomp_type (tree);
HOST_WIDE_INT cp_decomp_size (location_t, tree, tsubst_flags_t);
extern bool cp_finish_decomp (tree, cp_decomp *, bool = false);
extern int cp_complete_array_type (tree *, tree, bool);
extern int cp_complete_array_type_or_error (tree *, tree, bool, tsubst_flags_t);
@ -7760,8 +7779,12 @@ extern tree clone_attrs (tree);
extern bool maybe_clone_body (tree);
/* In parser.cc */
extern tree cp_build_range_for_decls (location_t, tree, tree *, bool);
extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
tree, bool);
extern tree build_range_temp (tree);
extern tree cp_perform_range_for_lookup (tree, tree *, tree *,
tsubst_flags_t = tf_warning_or_error);
extern void cp_convert_omp_range_for (tree &, tree &, tree &,
tree &, tree &, tree &, tree &, tree &,
bool);
@ -7978,6 +8001,7 @@ extern tree add_to_template_args (tree, tree);
extern tree add_outermost_template_args (tree, tree);
extern tree add_extra_args (tree, tree, tsubst_flags_t, tree);
extern tree build_extra_args (tree, tree, tsubst_flags_t);
extern void finish_expansion_stmt (tree, tree, tsubst_flags_t, tree);
/* in rtti.cc */
/* A vector of all tinfo decls that haven't been emitted yet. */
@ -8078,6 +8102,7 @@ public:
extern int stmts_are_full_exprs_p (void);
extern void init_cp_semantics (void);
extern tree do_poplevel (tree);
extern tree do_pushlevel (scope_kind);
extern void break_maybe_infinite_loop (void);
extern void add_decl_expr (tree);
extern tree maybe_cleanup_point_expr_void (tree);
@ -8104,7 +8129,7 @@ extern void find_range_for_decls (tree[3]);
extern void finish_for_stmt (tree);
extern tree begin_range_for_stmt (tree, tree);
extern void finish_range_for_decl (tree, tree, tree);
extern void finish_range_for_stmt (tree);
extern tree begin_template_for_scope (tree *);
extern tree finish_break_stmt (void);
extern tree finish_continue_stmt (void);
extern tree begin_switch_stmt (void);

View File

@ -2137,6 +2137,29 @@ cxx_pretty_printer::statement (tree t)
pp_needs_newline (this) = true;
break;
case TEMPLATE_FOR_STMT:
pp_cxx_ws_string (this, "template for");
pp_space (this);
pp_cxx_left_paren (this);
if (TEMPLATE_FOR_INIT_STMT (t))
{
statement (TEMPLATE_FOR_INIT_STMT (t));
pp_needs_newline (this) = false;
pp_cxx_whitespace (this);
}
statement (TEMPLATE_FOR_DECL (t));
pp_space (this);
pp_needs_newline (this) = false;
pp_colon (this);
pp_space (this);
statement (TEMPLATE_FOR_EXPR (t));
pp_cxx_right_paren (this);
pp_newline_and_indent (this, 3);
statement (TEMPLATE_FOR_BODY (t));
pp_indentation (this) -= 3;
pp_needs_newline (this) = true;
break;
/* expression-statement:
expression(opt) ; */
case EXPR_STMT:

View File

@ -572,9 +572,9 @@ poplevel_named_label_1 (named_label_entry **slot, cp_binding_level *bl)
ent->in_stmt_expr = true;
break;
case sk_block:
if (level_for_constexpr_if (bl->level_chain))
if (level_for_constexpr_if (obl))
ent->in_constexpr_if = true;
else if (level_for_consteval_if (bl->level_chain))
else if (level_for_consteval_if (obl))
ent->in_consteval_if = true;
break;
default:
@ -4336,7 +4336,19 @@ finish_case_label (location_t loc, tree low_value, tree high_value)
tree label;
/* For templates, just add the case label; we'll do semantic
analysis at instantiation-time. */
analysis at instantiation-time. But diagnose case labels
in expansion statements with switch outside of it here. */
if (in_expansion_stmt)
for (cp_binding_level *b = current_binding_level;
b != switch_stack->level; b = b->level_chain)
if (b->kind == sk_template_for && b->this_entity)
{
auto_diagnostic_group d;
error ("jump to case label");
inform (EXPR_LOCATION (b->this_entity),
" enters %<template for%> statement");
return error_mark_node;
}
label = build_decl (loc, LABEL_DECL, NULL_TREE, void_type_node);
return add_stmt (build_case_label (low_value, high_value, label));
}
@ -9633,13 +9645,17 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
error has been diagnosed. */
static tree
find_decomp_class_base (location_t loc, tree type, tree ret)
find_decomp_class_base (location_t loc, tree type, tree ret,
tsubst_flags_t complain)
{
if (LAMBDA_TYPE_P (type))
{
auto_diagnostic_group d;
error_at (loc, "cannot decompose lambda closure type %qT", type);
inform (location_of (type), "lambda declared here");
if (complain & tf_error)
{
auto_diagnostic_group d;
error_at (loc, "cannot decompose lambda closure type %qT", type);
inform (location_of (type), "lambda declared here");
}
return error_mark_node;
}
@ -9653,6 +9669,8 @@ find_decomp_class_base (location_t loc, tree type, tree ret)
return type;
else if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
{
if ((complain & tf_error) == 0)
return error_mark_node;
auto_diagnostic_group d;
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
error_at (loc, "cannot decompose class type %qT because it has an "
@ -9665,6 +9683,8 @@ find_decomp_class_base (location_t loc, tree type, tree ret)
}
else if (!accessible_p (type, field, true))
{
if ((complain & tf_error) == 0)
return error_mark_node;
auto_diagnostic_group d;
error_at (loc, "cannot decompose inaccessible member %qD of %qT",
field, type);
@ -9686,28 +9706,32 @@ find_decomp_class_base (location_t loc, tree type, tree ret)
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
auto_diagnostic_group d;
tree t = find_decomp_class_base (loc, TREE_TYPE (base_binfo), ret);
tree t = find_decomp_class_base (loc, TREE_TYPE (base_binfo), ret,
complain);
if (t == error_mark_node)
{
inform (location_of (type), "in base class of %qT", type);
if (complain & tf_error)
inform (location_of (type), "in base class of %qT", type);
return error_mark_node;
}
if (t != NULL_TREE && t != ret)
{
if (ret == type)
{
error_at (loc, "cannot decompose class type %qT: both it and "
"its base class %qT have non-static data members",
type, t);
if (complain & tf_error)
error_at (loc, "cannot decompose class type %qT: both it and "
"its base class %qT have non-static data "
"members", type, t);
return error_mark_node;
}
else if (orig_ret != NULL_TREE)
return t;
else if (ret != NULL_TREE)
{
error_at (loc, "cannot decompose class type %qT: its base "
"classes %qT and %qT have non-static data "
"members", type, ret, t);
if (complain & tf_error)
error_at (loc, "cannot decompose class type %qT: its base "
"classes %qT and %qT have non-static data "
"members", type, ret, t);
return error_mark_node;
}
else
@ -9893,6 +9917,116 @@ set_sb_pack_name (tree decl, unsigned HOST_WIDE_INT i)
}
}
/* Return structured binding size of TYPE or -1 if erroneous. */
HOST_WIDE_INT
cp_decomp_size (location_t loc, tree type, tsubst_flags_t complain)
{
if (TYPE_REF_P (type))
{
type = complete_type (TREE_TYPE (type));
if (type == error_mark_node)
return -1;
if (!COMPLETE_TYPE_P (type))
{
if (complain & tf_error)
error_at (loc, "structured binding refers to incomplete type %qT",
type);
return -1;
}
}
unsigned HOST_WIDE_INT eltscnt = 0;
if (TREE_CODE (type) == ARRAY_TYPE)
{
if (TYPE_DOMAIN (type) == NULL_TREE)
{
if (complain & tf_error)
error_at (loc, "cannot decompose array of unknown bound %qT",
type);
return -1;
}
tree nelts = array_type_nelts_top (type);
if (nelts == error_mark_node)
return -1;
if (!tree_fits_shwi_p (nelts))
{
if (complain & tf_error)
error_at (loc, "cannot decompose variable length array %qT", type);
return -1;
}
return tree_to_shwi (nelts);
}
/* 2 GNU extensions. */
else if (TREE_CODE (type) == COMPLEX_TYPE)
return 2;
else if (TREE_CODE (type) == VECTOR_TYPE)
{
if (!TYPE_VECTOR_SUBPARTS (type).is_constant (&eltscnt))
{
if (complain & tf_error)
error_at (loc, "cannot decompose variable length vector %qT", type);
return -1;
}
return eltscnt;
}
else if (tree tsize = get_tuple_size (type))
{
if (tsize == error_mark_node
|| !tree_fits_shwi_p (tsize)
|| tree_int_cst_sgn (tsize) < 0)
{
if (complain & tf_error)
error_at (loc, "%<std::tuple_size<%T>::value%> is not an integral "
"constant expression", type);
return -1;
}
return tree_to_shwi (tsize);
}
else if (TREE_CODE (type) == UNION_TYPE)
{
if (complain & tf_error)
error_at (loc, "cannot decompose union type %qT", type);
return -1;
}
else if (!CLASS_TYPE_P (type))
{
if (complain & tf_error)
error_at (loc, "cannot decompose non-array non-class type %qT", type);
return -1;
}
else if (processing_template_decl && complete_type (type) == error_mark_node)
return -1;
else if (processing_template_decl && !COMPLETE_TYPE_P (type))
{
if (complain & tf_error)
pedwarn (loc, 0, "structured binding refers to incomplete class type "
"%qT", type);
return -1;
}
else
{
tree btype = find_decomp_class_base (loc, type, NULL_TREE, complain);
if (btype == error_mark_node)
return -1;
else if (btype == NULL_TREE)
{
if (complain & tf_error)
error_at (loc, "cannot decompose class type %qT without non-static "
"data members", type);
return -1;
}
for (tree field = TYPE_FIELDS (btype); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) != FIELD_DECL
|| DECL_ARTIFICIAL (field)
|| DECL_UNNAMED_BIT_FIELD (field))
continue;
else
eltscnt++;
return eltscnt;
}
}
/* Finish a decomposition declaration. DECL is the underlying declaration
"e", FIRST is the head of a chain of decls for the individual identifiers
chained through DECL_CHAIN in reverse order and COUNT is the number of
@ -10319,7 +10453,8 @@ cp_finish_decomp (tree decl, cp_decomp *decomp, bool test_p)
type);
else
{
tree btype = find_decomp_class_base (loc, type, NULL_TREE);
tree btype = find_decomp_class_base (loc, type, NULL_TREE,
tf_warning_or_error);
if (btype == error_mark_node)
goto error_out;
else if (btype == NULL_TREE)

View File

@ -6229,6 +6229,7 @@ cp_warn_deprecated_use_scopes (tree scope)
bool
decl_dependent_p (tree decl)
{
tree orig_decl = decl;
if (DECL_FUNCTION_SCOPE_P (decl)
|| TREE_CODE (decl) == CONST_DECL
|| TREE_CODE (decl) == USING_DECL
@ -6240,6 +6241,13 @@ decl_dependent_p (tree decl)
if (LAMBDA_FUNCTION_P (decl)
&& dependent_type_p (DECL_CONTEXT (decl)))
return true;
/* for-range-declaration of expansion statement as well as variable
declarations in the expansion statement body when the expansion statement
is not inside a template still need to be treated as dependent during
parsing. When the body is instantiated, in_expansion_stmt will be already
false. */
if (VAR_P (orig_decl) && in_expansion_stmt && decl == current_function_decl)
return true;
return false;
}

View File

@ -3953,14 +3953,20 @@ print_instantiation_full_context (diagnostics::text_sink &text_output)
= ((!text_output.show_nesting_p ())
|| text_output.show_locations_in_nesting_p ());
char *indent = text_output.build_indent_prefix (true);
bool expansion_stmt_p = TREE_CODE (p->tldcl) == TEMPLATE_FOR_STMT;
pp_verbatim (text_output.get_printer (),
p->list_p ()
expansion_stmt_p
? G_("%s%s%sIn instantiation of %<template for%> "
"iteration %E:\n")
: p->list_p ()
? G_("%s%s%sIn substitution of %qS:\n")
: G_("%s%s%sIn instantiation of %q#D:\n"),
indent,
show_file ? LOCATION_FILE (location) : "",
show_file ? ": " : "",
p->get_node ());
expansion_stmt_p
? TREE_VEC_ELT (p->targs, 0)
: p->get_node ());
free (indent);
location = p->locus;
p = p->next;
@ -4069,7 +4075,14 @@ print_instantiation_partial_context_line (diagnostics::text_sink &text_output,
if (t != NULL)
{
if (t->list_p ())
if (TREE_CODE (t->tldcl) == TEMPLATE_FOR_STMT)
pp_verbatim (pp,
recursive_p
? G_("recursively required from %<template for%> "
"iteration %E\n")
: G_("required from %<template for%> iteration %E\n"),
TREE_VEC_ELT (t->targs, 0));
else if (t->list_p ())
pp_verbatim (pp,
recursive_p
? G_("recursively required by substitution of %qS\n")

View File

@ -3408,7 +3408,9 @@ check_local_shadow (tree decl)
detected elsewhere. */
else if (VAR_P (old)
&& old_scope == current_binding_level->level_chain
&& (old_scope->kind == sk_cond || old_scope->kind == sk_for))
&& (old_scope->kind == sk_cond
|| old_scope->kind == sk_for
|| old_scope->kind == sk_template_for))
{
if (name_independent_decl_p (decl))
return old;
@ -4626,6 +4628,7 @@ cp_binding_level_descriptor (cp_binding_level *scope)
"try-scope",
"catch-scope",
"for-scope",
"template-for-scope",
"cond-init-scope",
"stmt-expr-scope",
"function-parameter-scope",
@ -4720,6 +4723,7 @@ begin_scope (scope_kind kind, tree entity)
case sk_try:
case sk_catch:
case sk_for:
case sk_template_for:
case sk_cond:
case sk_class:
case sk_scoped_enum:

View File

@ -198,6 +198,7 @@ enum scope_kind {
sk_catch, /* A catch-block. */
sk_for, /* The scope of the variable declared in a
init-statement. */
sk_template_for, /* Ditto for expansion statements. */
sk_cond, /* The scope of the variable declared in the condition
of an if or switch statement. */
sk_stmt_expr, /* GNU statement expression block. */
@ -287,7 +288,7 @@ struct GTY(()) cp_binding_level {
/* The kind of scope that this object represents. However, a
SK_TEMPLATE_SPEC scope is represented with KIND set to
SK_TEMPLATE_PARMS and EXPLICIT_SPEC_P set to true. */
ENUM_BITFIELD (scope_kind) kind : 4;
ENUM_BITFIELD (scope_kind) kind : 5;
/* True if this scope is an SK_TEMPLATE_SPEC scope. This field is
only valid if KIND == SK_TEMPLATE_PARMS. */
@ -315,7 +316,7 @@ struct GTY(()) cp_binding_level {
parent scope. */
unsigned artificial : 1;
/* 21 bits left to fill a 32-bit word. */
/* 20 bits left to fill a 32-bit word. */
};
/* The binding level currently in effect. */

View File

@ -601,6 +601,8 @@ cp_debug_parser (FILE *file, cp_parser *parser)
parser->in_template_argument_list_p);
cp_debug_print_flag (file, "Parsing an iteration statement",
parser->in_statement & IN_ITERATION_STMT);
cp_debug_print_flag (file, "Parsing an expansion statement",
parser->in_statement & IN_EXPANSION_STMT);
cp_debug_print_flag (file, "Parsing a switch statement",
parser->in_statement & IN_SWITCH_STMT);
cp_debug_print_flag (file, "Parsing a structured OpenMP block",
@ -2611,11 +2613,11 @@ static tree cp_parser_c_for
static tree cp_parser_range_for
(cp_parser *, tree, tree, tree, bool, tree, bool, bool);
static void do_range_for_auto_deduction
(tree, tree, cp_decomp *);
static tree cp_parser_perform_range_for_lookup
(tree, tree *, tree *);
static tree cp_parser_range_for_member_function
(tree, tree, cp_decomp *, bool);
static tree cp_range_for_member_function
(tree, tree);
static tree cp_parser_expansion_statement
(cp_parser *, bool *);
static tree cp_parser_jump_statement
(cp_parser *, tree &);
static void cp_parser_declaration_statement
@ -11875,6 +11877,10 @@ cp_parser_lambda_expression (cp_parser* parser,
bool save_in_consteval_if_p = in_consteval_if_p;
in_consteval_if_p = false;
/* Similarly the body of a lambda is not part of expansion statement. */
bool save_in_expansion_stmt = in_expansion_stmt;
in_expansion_stmt = 0;
/* By virtue of defining a local class, a lambda expression has access to
the private variables of enclosing classes. */
@ -11904,6 +11910,7 @@ cp_parser_lambda_expression (cp_parser* parser,
finish_struct (type, /*attributes=*/NULL_TREE);
in_expansion_stmt = save_in_expansion_stmt;
in_consteval_if_p = save_in_consteval_if_p;
in_discarded_stmt = discarded;
@ -13164,6 +13171,11 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
case RID_TRANSACTION_CANCEL:
handle_omp_attribs = true;
break;
case RID_TEMPLATE:
if (cxx_dialect >= cxx11
&& cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_FOR))
handle_omp_attribs = true;
break;
default:
break;
}
@ -13216,6 +13228,16 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
NULL_TREE, false);
break;
case RID_TEMPLATE:
if (cxx_dialect >= cxx11
&& cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_FOR))
{
std_attrs = process_stmt_hotness_attribute (std_attrs,
attrs_loc);
statement = cp_parser_expansion_statement (parser, if_p);
}
break;
case RID_BREAK:
case RID_CONTINUE:
case RID_RETURN:
@ -13618,6 +13640,13 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes)
default:
/* Anything else must be an ordinary label. */
cp_expr identifier = cp_parser_identifier (parser);
if (in_expansion_stmt && identifier != error_mark_node)
{
error_at (token->location,
"identifier label %qE in %<template for%> body",
*identifier);
break;
}
if (identifier != error_mark_node
&& parser->omp_metadirective_state)
*identifier = mangle_metadirective_region_label (parser, *identifier);
@ -14644,6 +14673,73 @@ cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
return stmt;
}
/* Helper function for cp_parser_range_for and cp_parser_expansion_statement.
Get the range declaration momentarily out of the way so that the range
expression doesn't clash with it. */
static cp_decomp *
cp_hide_range_decl (tree *range_decl_p, cp_decomp *decomp_d,
auto_vec <cxx_binding *> &bindings,
auto_vec <tree> &names)
{
tree range_decl = *range_decl_p;
cp_decomp *decomp = NULL;
if (range_decl == error_mark_node)
return decomp;
if (DECL_HAS_VALUE_EXPR_P (range_decl))
{
tree v = DECL_VALUE_EXPR (range_decl);
/* For decomposition declaration get all of the corresponding
declarations out of the way. */
if ((TREE_CODE (v) == ARRAY_REF
&& DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
|| (TREE_CODE (v) == TREE_VEC
&& DECL_DECOMPOSITION_P (TREE_VEC_ELT (v, 0))))
{
tree d = range_decl;
decomp = decomp_d;
if (TREE_CODE (v) == ARRAY_REF)
{
*range_decl_p = range_decl = TREE_OPERAND (v, 0);
decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
}
else
{
*range_decl_p = range_decl = TREE_VEC_ELT (v, 0);
decomp->count = tree_to_uhwi (TREE_VEC_ELT (v, 1)) + 1;
}
decomp->decl = d;
bool seen_name_independent_decl = false;
names.reserve (decomp->count);
bindings.reserve (decomp->count);
for (unsigned int i = 0; i < decomp->count; i++, d = DECL_CHAIN (d))
{
if (name_independent_decl_p (d))
{
/* If there is more than one _ decl in the structured
binding, just push and move it away once. */
if (seen_name_independent_decl)
continue;
seen_name_independent_decl = true;
}
tree name = DECL_NAME (d);
names.quick_push (name);
bindings.quick_push (IDENTIFIER_BINDING (name));
IDENTIFIER_BINDING (name) = IDENTIFIER_BINDING (name)->previous;
}
}
}
if (names.is_empty ())
{
tree name = DECL_NAME (range_decl);
names.safe_push (name);
bindings.safe_push (IDENTIFIER_BINDING (name));
IDENTIFIER_BINDING (name) = IDENTIFIER_BINDING (name)->previous;
}
return decomp;
}
/* Tries to parse a range-based for-statement:
range-based-for:
@ -14659,66 +14755,14 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
bool ivdep, tree unroll, bool novector, bool is_omp)
{
tree stmt, range_expr;
auto_vec <cxx_binding *, 16> bindings;
auto_vec <tree, 16> names;
cp_decomp decomp_d, *decomp = NULL;
auto_vec <cxx_binding *> bindings;
auto_vec <tree> names;
cp_decomp decomp_d;
/* Get the range declaration momentarily out of the way so that
the range expression doesn't clash with it. */
if (range_decl != error_mark_node)
{
if (DECL_HAS_VALUE_EXPR_P (range_decl))
{
tree v = DECL_VALUE_EXPR (range_decl);
/* For decomposition declaration get all of the corresponding
declarations out of the way. */
if ((TREE_CODE (v) == ARRAY_REF
&& DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
|| (TREE_CODE (v) == TREE_VEC
&& DECL_DECOMPOSITION_P (TREE_VEC_ELT (v, 0))))
{
tree d = range_decl;
decomp = &decomp_d;
if (TREE_CODE (v) == ARRAY_REF)
{
range_decl = TREE_OPERAND (v, 0);
decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
}
else
{
range_decl = TREE_VEC_ELT (v, 0);
decomp->count = tree_to_uhwi (TREE_VEC_ELT (v, 1)) + 1;
}
decomp->decl = d;
bool seen_name_independent_decl = false;
for (unsigned int i = 0; i < decomp->count;
i++, d = DECL_CHAIN (d))
{
if (name_independent_decl_p (d))
{
/* If there is more than one _ decl in
the structured binding, just push and move it
away once. */
if (seen_name_independent_decl)
continue;
seen_name_independent_decl = true;
}
tree name = DECL_NAME (d);
names.safe_push (name);
bindings.safe_push (IDENTIFIER_BINDING (name));
IDENTIFIER_BINDING (name)
= IDENTIFIER_BINDING (name)->previous;
}
}
}
if (names.is_empty ())
{
tree name = DECL_NAME (range_decl);
names.safe_push (name);
bindings.safe_push (IDENTIFIER_BINDING (name));
IDENTIFIER_BINDING (name) = IDENTIFIER_BINDING (name)->previous;
}
}
cp_decomp *decomp = cp_hide_range_decl (&range_decl, &decomp_d, bindings,
names);
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
range_expr = cp_parser_braced_list (parser);
@ -14755,7 +14799,7 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
if (!type_dependent_expression_p (range_expr)
/* do_auto_deduction doesn't mess with template init-lists. */
&& !BRACE_ENCLOSED_INITIALIZER_P (range_expr))
do_range_for_auto_deduction (range_decl, range_expr, decomp);
do_range_for_auto_deduction (range_decl, range_expr, decomp, false);
}
else
{
@ -14769,7 +14813,7 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
/* Subroutine of cp_convert_range_for: given the initializer expression,
builds up the range temporary. */
static tree
tree
build_range_temp (tree range_expr)
{
/* Find out the type deduced by the declaration
@ -14793,15 +14837,22 @@ build_range_temp (tree range_expr)
a shortcut version of cp_convert_range_for. */
static void
do_range_for_auto_deduction (tree decl, tree range_expr, cp_decomp *decomp)
do_range_for_auto_deduction (tree decl, tree range_expr, cp_decomp *decomp,
bool expansion_stmt)
{
tree auto_node = type_uses_auto (TREE_TYPE (decl));
if (auto_node)
{
tree begin_dummy, end_dummy, range_temp, iter_type, iter_decl;
range_temp = convert_from_reference (build_range_temp (range_expr));
iter_type = (cp_parser_perform_range_for_lookup
(range_temp, &begin_dummy, &end_dummy));
iter_type = cp_perform_range_for_lookup (range_temp, &begin_dummy,
&end_dummy,
expansion_stmt ? tf_none
: tf_warning_or_error);
if (expansion_stmt
&& (begin_dummy == error_mark_node
|| end_dummy == error_mark_node))
return;
if (iter_type)
{
iter_decl = build_decl (input_location, VAR_DECL, NULL_TREE,
@ -14902,6 +14953,89 @@ warn_for_range_copy (tree decl, tree expr)
}
}
/* Helper function for cp_convert_range_for and finish_expansion_stmt.
Build the __range, __begin and __end declarations. Return the
__begin VAR_DECL, set *END_P to the __end VAR_DECL. */
tree
cp_build_range_for_decls (location_t loc, tree range_expr, tree *end_p,
bool expansion_stmt_p)
{
tree iter_type, begin_expr, end_expr;
if (range_expr == error_mark_node)
/* If an error happened previously do nothing or else a lot of
unhelpful errors would be issued. */
begin_expr = end_expr = iter_type = error_mark_node;
else
{
tree range_temp;
if (!expansion_stmt_p
&& VAR_P (range_expr)
&& array_of_runtime_bound_p (TREE_TYPE (range_expr)))
/* Can't bind a reference to an array of runtime bound. */
range_temp = range_expr;
else
{
range_temp = build_range_temp (range_expr);
if (expansion_stmt_p)
{
/* Depending on CWG3044 resolution, we might want to remove
these 3 sets of TREE_STATIC (on range_temp, begin and end).
Although it can only be done when P2686R4 is fully
implemented. */
TREE_STATIC (range_temp) = 1;
TREE_PUBLIC (range_temp) = 0;
DECL_COMMON (range_temp) = 0;
DECL_INTERFACE_KNOWN (range_temp) = 1;
DECL_DECLARED_CONSTEXPR_P (range_temp) = 1;
TREE_READONLY (range_temp) = 1;
}
pushdecl (range_temp);
cp_finish_decl (range_temp, range_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
range_temp = convert_from_reference (range_temp);
}
iter_type = cp_perform_range_for_lookup (range_temp, &begin_expr,
&end_expr);
}
/* The new for initialization statement. */
tree begin = build_decl (loc, VAR_DECL, for_begin__identifier, iter_type);
TREE_USED (begin) = 1;
DECL_ARTIFICIAL (begin) = 1;
if (expansion_stmt_p)
{
TREE_STATIC (begin) = 1;
DECL_DECLARED_CONSTEXPR_P (begin) = 1;
TREE_READONLY (begin) = 1;
}
pushdecl (begin);
cp_finish_decl (begin, begin_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
if (cxx_dialect >= cxx17)
iter_type = cv_unqualified (TREE_TYPE (end_expr));
tree end = build_decl (loc, VAR_DECL, for_end__identifier, iter_type);
TREE_USED (end) = 1;
DECL_ARTIFICIAL (end) = 1;
if (expansion_stmt_p)
{
TREE_STATIC (end) = 1;
DECL_DECLARED_CONSTEXPR_P (end) = 1;
TREE_READONLY (end) = 1;
}
pushdecl (end);
cp_finish_decl (end, end_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
*end_p = end;
return begin;
}
/* Converts a range-based for-statement into a normal
for-statement, as per the definition.
@ -14942,56 +15076,14 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
cp_decomp *decomp, bool ivdep, tree unroll,
bool novector)
{
tree begin, end;
tree iter_type, begin_expr, end_expr;
tree condition, expression;
tree end, condition, expression;
range_expr = mark_lvalue_use (range_expr);
if (range_decl == error_mark_node || range_expr == error_mark_node)
/* If an error happened previously do nothing or else a lot of
unhelpful errors would be issued. */
begin_expr = end_expr = iter_type = error_mark_node;
else
{
tree range_temp;
if (VAR_P (range_expr)
&& array_of_runtime_bound_p (TREE_TYPE (range_expr)))
/* Can't bind a reference to an array of runtime bound. */
range_temp = range_expr;
else
{
range_temp = build_range_temp (range_expr);
pushdecl (range_temp);
cp_finish_decl (range_temp, range_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
range_temp = convert_from_reference (range_temp);
}
iter_type = cp_parser_perform_range_for_lookup (range_temp,
&begin_expr, &end_expr);
}
/* The new for initialization statement. */
begin = build_decl (input_location, VAR_DECL, for_begin__identifier,
iter_type);
TREE_USED (begin) = 1;
DECL_ARTIFICIAL (begin) = 1;
pushdecl (begin);
cp_finish_decl (begin, begin_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
if (cxx_dialect >= cxx17)
iter_type = cv_unqualified (TREE_TYPE (end_expr));
end = build_decl (input_location, VAR_DECL, for_end__identifier, iter_type);
TREE_USED (end) = 1;
DECL_ARTIFICIAL (end) = 1;
pushdecl (end);
cp_finish_decl (end, end_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
if (range_decl == error_mark_node)
range_expr = error_mark_node;
tree begin
= cp_build_range_for_decls (input_location, range_expr, &end, false);
finish_init_stmt (statement);
@ -15025,8 +15117,10 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
depends on the existence of members begin or end.
Returns the type deduced for the iterator expression. */
static tree
cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
tree
cp_perform_range_for_lookup (tree range, tree *begin, tree *end,
tsubst_flags_t complain
/* = tf_warning_or_error */)
{
if (error_operand_p (range))
{
@ -15036,8 +15130,9 @@ cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (range))))
{
error ("range-based %<for%> expression of type %qT "
"has incomplete type", TREE_TYPE (range));
if (complain & tf_error)
error ("range-based %<for%> expression of type %qT "
"has incomplete type", TREE_TYPE (range));
*begin = *end = error_mark_node;
return error_mark_node;
}
@ -15063,16 +15158,16 @@ cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
id_end = get_identifier ("end");
member_begin = lookup_member (TREE_TYPE (range), id_begin,
/*protect=*/2, /*want_type=*/false,
tf_warning_or_error);
complain);
member_end = lookup_member (TREE_TYPE (range), id_end,
/*protect=*/2, /*want_type=*/false,
tf_warning_or_error);
complain);
if (member_begin != NULL_TREE && member_end != NULL_TREE)
{
/* Use the member functions. */
*begin = cp_parser_range_for_member_function (range, id_begin);
*end = cp_parser_range_for_member_function (range, id_end);
*begin = cp_range_for_member_function (range, id_begin);
*end = cp_range_for_member_function (range, id_end);
}
else
{
@ -15082,13 +15177,20 @@ cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
vec_safe_push (vec, range);
member_begin = perform_koenig_lookup (id_begin, vec,
tf_warning_or_error);
complain);
if ((complain & tf_error) == 0 && member_begin == id_begin)
return error_mark_node;
*begin = finish_call_expr (member_begin, &vec, false, true,
tf_warning_or_error);
complain);
member_end = perform_koenig_lookup (id_end, vec,
tf_warning_or_error);
if ((complain & tf_error) == 0 && member_end == id_end)
{
*begin = error_mark_node;
return error_mark_node;
}
*end = finish_call_expr (member_end, &vec, false, true,
tf_warning_or_error);
complain);
}
/* Last common checks. */
@ -15119,7 +15221,7 @@ cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
/* P0184R0 allows __begin and __end to have different types,
but make sure they are comparable so we can give a better
diagnostic. */;
else
else if (complain & tf_error)
error ("inconsistent begin/end types in range-based %<for%> "
"statement: %qT and %qT",
TREE_TYPE (*begin), TREE_TYPE (*end));
@ -15129,11 +15231,11 @@ cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end)
}
}
/* Helper function for cp_parser_perform_range_for_lookup.
/* Helper function for cp_perform_range_for_lookup.
Builds a tree for RANGE.IDENTIFIER(). */
static tree
cp_parser_range_for_member_function (tree range, tree identifier)
cp_range_for_member_function (tree range, tree identifier)
{
tree member, res;
@ -15352,6 +15454,183 @@ cp_parser_init_statement (cp_parser *parser, tree *decl)
return false;
}
/* Parse an expansion-statement.
expansion-statement:
template for ( init-statement[opt]
for-range-declaration : expansion-initializer )
statement
expansion-initializer:
expression
expansion-init-list
expansion-init-list:
{ expression-list } */
static tree
cp_parser_expansion_statement (cp_parser* parser, bool *if_p)
{
/* Peek at the next token. */
cp_token *token = cp_lexer_peek_token (parser->lexer);
gcc_assert (token->keyword == RID_TEMPLATE);
gcc_assert (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_FOR));
cp_lexer_consume_token (parser->lexer);
cp_token *for_token = cp_lexer_peek_token (parser->lexer);
cp_lexer_consume_token (parser->lexer);
if (cxx_dialect < cxx26)
pedwarn (make_location (token->location, token->location,
for_token->location), OPT_Wc__26_extensions,
"%<template for%> only available with %<-std=c++2c%> "
"or %<-std=gnu++2c%>");
token_indent_info guard_tinfo = get_token_indent_info (token);
/* Remember whether or not we are already within an iteration
statement. */
unsigned char in_statement = parser->in_statement;
/* And whether we are already in expansion-statement. */
auto save_in_expansion_stmt = in_expansion_stmt;
/* Look for the `('. */
matching_parens parens;
parens.require_open (parser);
tree init;
tree scope = begin_template_for_scope (&init);
/* Maybe parse the optional init-statement in a expansion-statement. */
if (cp_parser_range_based_for_with_init_p (parser)
/* Checked for diagnostic purposes only. */
&& cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
{
tree dummy;
cp_parser_init_statement (parser, &dummy);
}
bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
/* A colon is used in expansion-statement. */
parser->colon_corrects_to_scope_p = false;
/* Parse the declaration. */
tree range_decl;
cp_parser_simple_declaration (parser,
/*function_definition_allowed_p=*/false,
&range_decl);
if (range_decl == NULL_TREE)
range_decl = error_mark_node;
parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
cp_parser_require (parser, CPP_COLON, RT_COLON);
auto_vec <cxx_binding *> bindings;
auto_vec <tree> names;
cp_decomp decomp_d;
/* Get the range declaration momentarily out of the way so that
the range expression doesn't clash with it. */
cp_decomp *decomp = cp_hide_range_decl (&range_decl, &decomp_d, bindings,
names);
tree expansion_init;
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
{
expansion_init = cp_parser_braced_list (parser);
if (TREE_CODE (expansion_init) == CONSTRUCTOR
&& CONSTRUCTOR_IS_DESIGNATED_INIT (expansion_init))
error_at (EXPR_LOC_OR_LOC (expansion_init, token->location),
"designators in %<template for%> initializer");
}
else
expansion_init = cp_parser_expression (parser);
/* Put the range declaration(s) back into scope. */
for (unsigned int i = 0; i < names.length (); i++)
{
cxx_binding *binding = bindings[i];
binding->previous = IDENTIFIER_BINDING (names[i]);
IDENTIFIER_BINDING (names[i]) = binding;
}
/* Look for the `)'. */
parens.require_close (parser);
if (processing_template_decl
&& check_for_bare_parameter_packs (expansion_init))
expansion_init = error_mark_node;
if (expansion_init != error_mark_node
&& !type_dependent_expression_p (expansion_init)
&& TREE_CODE (TREE_TYPE (expansion_init)) != ARRAY_TYPE
&& !BRACE_ENCLOSED_INITIALIZER_P (expansion_init))
do_range_for_auto_deduction (range_decl, expansion_init, decomp,
true);
bool outside_of_template = !processing_template_decl;
if (outside_of_template)
{
++processing_template_decl;
current_template_parms
= tree_cons (size_int (current_template_depth + 1),
make_tree_vec (0), current_template_parms);
}
in_expansion_stmt = true;
tree r = build_stmt (token->location, TEMPLATE_FOR_STMT, NULL_TREE,
NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
current_binding_level->this_entity = r;
TEMPLATE_FOR_INIT_STMT (r) = init;
TEMPLATE_FOR_SCOPE (r) = scope;
if (!outside_of_template)
TEMPLATE_FOR_INIT_STMT (r) = pop_stmt_list (TEMPLATE_FOR_INIT_STMT (r));
TEMPLATE_FOR_DECL (r) = range_decl;
TEMPLATE_FOR_EXPR (r) = expansion_init;
TEMPLATE_FOR_BODY (r) = do_pushlevel (sk_block);
/* Parse the body of the expansion-statement. */
parser->in_statement = IN_EXPANSION_STMT;
bool prev = note_iteration_stmt_body_start ();
cp_parser_already_scoped_statement (parser, if_p, guard_tinfo);
note_iteration_stmt_body_end (prev);
parser->in_statement = in_statement;
in_expansion_stmt = save_in_expansion_stmt;
TEMPLATE_FOR_BODY (r) = do_poplevel (TEMPLATE_FOR_BODY (r));
if (outside_of_template)
{
current_template_parms = TREE_CHAIN (current_template_parms);
--processing_template_decl;
}
if (VAR_P (range_decl) && DECL_DECLARED_CONSTINIT_P (range_decl))
error_at (DECL_SOURCE_LOCATION (range_decl),
"for-range-declaration cannot be 'constinit'");
if (decomp)
{
tree v = make_tree_vec (decomp->count + 1);
TREE_VEC_ELT (v, 0) = TEMPLATE_FOR_DECL (r);
tree d = decomp->decl;
for (unsigned i = 0; i < decomp->count; ++i, d = DECL_CHAIN (d))
TREE_VEC_ELT (v, decomp->count - i) = d;
TEMPLATE_FOR_DECL (r) = v;
}
if (processing_template_decl)
add_stmt (r);
else
finish_expansion_stmt (r, NULL_TREE, tf_warning_or_error, NULL_TREE);
add_stmt (do_poplevel (TEMPLATE_FOR_SCOPE (r)));
TEMPLATE_FOR_SCOPE (r) = NULL_TREE;
return r;
}
/* Parse a jump-statement.
jump-statement:
@ -15396,7 +15675,8 @@ cp_parser_jump_statement (cp_parser* parser, tree &std_attrs)
break;
default:
gcc_assert ((in_statement & IN_SWITCH_STMT)
|| in_statement == IN_ITERATION_STMT);
|| in_statement == IN_ITERATION_STMT
|| in_statement == IN_EXPANSION_STMT);
statement = finish_break_stmt ();
if (in_statement == IN_ITERATION_STMT)
break_maybe_infinite_loop ();
@ -15419,6 +15699,7 @@ cp_parser_jump_statement (cp_parser* parser, tree &std_attrs)
break;
/* Fall through. */
case IN_ITERATION_STMT:
case IN_EXPANSION_STMT:
case IN_OMP_FOR:
statement = finish_continue_stmt ();
break;
@ -46362,7 +46643,7 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
decomp->decl = decl;
}
}
do_range_for_auto_deduction (d, init, decomp);
do_range_for_auto_deduction (d, init, decomp, false);
}
cond = global_namespace;
incr = NULL_TREE;
@ -46418,8 +46699,8 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
range_temp_decl = range_temp;
range_temp = convert_from_reference (range_temp);
}
iter_type = cp_parser_perform_range_for_lookup (range_temp,
&begin_expr, &end_expr);
iter_type = cp_perform_range_for_lookup (range_temp, &begin_expr,
&end_expr);
}
tree end_iter_type = iter_type;

View File

@ -328,14 +328,16 @@ struct GTY(()) cp_parser {
/* Set to IN_ITERATION_STMT if parsing an iteration-statement,
to IN_OMP_BLOCK if parsing OpenMP structured block and
IN_OMP_FOR if parsing OpenMP loop. If parsing a switch statement,
IN_OMP_FOR if parsing OpenMP loop, IN_EXPANSION_STMT if parsing an
expansion-statement. If parsing a switch statement,
this is bitwise ORed with IN_SWITCH_STMT, unless parsing an
iteration-statement, OpenMP block or loop within that switch. */
#define IN_SWITCH_STMT 1
#define IN_ITERATION_STMT 2
#define IN_OMP_BLOCK 4
#define IN_OMP_FOR 8
#define IN_IF_STMT 16
#define IN_IF_STMT 16
#define IN_EXPANSION_STMT 32
unsigned char in_statement;
/* TRUE if we are presently parsing the body of a switch statement.

View File

@ -11453,7 +11453,8 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
#pragma GCC diagnostic ignored "-Wformat-diag"
#endif
bool list_p = new_level->list_p ();
if (list_p && !pp.has_flag (TDF_DETAILS))
if ((list_p || TREE_CODE (tldcl) == TEMPLATE_FOR_STMT)
&& !pp.has_flag (TDF_DETAILS))
/* Skip non-instantiations unless -details. */;
else
{
@ -11469,7 +11470,9 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
}
for (int i = 0; i < tinst_depth; ++i)
pp_space (&pp);
if (list_p)
if (TREE_CODE (tldcl) == TEMPLATE_FOR_STMT)
pp_printf (&pp, "I template for");
else if (list_p)
pp_printf (&pp, "S %S", new_level->get_node ());
else
pp_printf (&pp, "I %D", tldcl);
@ -11583,7 +11586,9 @@ reopen_tinst_level (struct tinst_level *level)
{
last_ctx = ctx;
pp_newline (&pp);
if (t->list_p ())
if (TREE_CODE (t->tldcl) == TEMPLATE_FOR_STMT)
pp_printf (&pp, "RI template for");
else if (t->list_p ())
pp_printf (&pp, "RS %S", ctx);
else
pp_printf (&pp, "RI %D", ctx);
@ -19378,6 +19383,62 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
finish_do_stmt (tmp, stmt, false, 0, false);
break;
case TEMPLATE_FOR_STMT:
{
tree init;
stmt = build_stmt (EXPR_LOCATION (t), TEMPLATE_FOR_STMT, NULL_TREE,
NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
TEMPLATE_FOR_SCOPE (stmt) = begin_template_for_scope (&init);
TEMPLATE_FOR_INIT_STMT (stmt) = init;
RECUR (TEMPLATE_FOR_INIT_STMT (t));
TEMPLATE_FOR_EXPR (stmt) = RECUR (TEMPLATE_FOR_EXPR (t));
if (processing_template_decl)
{
tree orig_decl = TEMPLATE_FOR_DECL (t);
if (TREE_CODE (orig_decl) == TREE_VEC)
orig_decl = TREE_VEC_ELT (orig_decl, 0);
tree decl = tsubst (orig_decl, args, complain, in_decl);
maybe_push_decl (decl);
cp_decomp decomp_d, *decomp = NULL;
if (DECL_DECOMPOSITION_P (decl))
{
decomp = &decomp_d;
decl = tsubst_decomp_names (decl, orig_decl, args,
complain, in_decl, decomp);
if (decl != error_mark_node)
{
tree v = make_tree_vec (decomp->count + 1);
TREE_VEC_ELT (v, 0) = decl;
decl = decomp->decl;
for (unsigned i = 0; i < decomp->count; ++i)
{
TREE_VEC_ELT (v, decomp->count - i) = decl;
decl = DECL_CHAIN (decl);
}
decl = v;
}
}
TEMPLATE_FOR_DECL (stmt) = decl;
TEMPLATE_FOR_INIT_STMT (stmt) = pop_stmt_list (init);
add_stmt (stmt);
TEMPLATE_FOR_BODY (stmt) = do_pushlevel (sk_block);
bool prev = note_iteration_stmt_body_start ();
RECUR (TEMPLATE_FOR_BODY (t));
note_iteration_stmt_body_end (prev);
TEMPLATE_FOR_BODY (stmt)
= do_poplevel (TEMPLATE_FOR_BODY (stmt));
}
else
{
TEMPLATE_FOR_DECL (stmt) = TEMPLATE_FOR_DECL (t);
TEMPLATE_FOR_BODY (stmt) = TEMPLATE_FOR_BODY (t);
finish_expansion_stmt (stmt, args, complain, in_decl);
}
add_stmt (do_poplevel (TEMPLATE_FOR_SCOPE (stmt)));
}
break;
case IF_STMT:
stmt = begin_if_stmt ();
IF_STMT_CONSTEXPR_P (stmt) = IF_STMT_CONSTEXPR_P (t);
@ -32552,6 +32613,405 @@ add_mergeable_specialization (bool decl_p, spec_entry *elt, tree decl,
}
}
struct expansion_stmt_bc
{
tree break_label;
tree continue_label;
hash_set<tree> *pset;
location_t loc;
bool in_switch;
};
/* Helper function for finish_expansion_stmt. Find BREAK_STMT (not
nested inside of other WHILE_STMT, FOR_STMT, DO_STMT, TEMPLATE_FOR_STMT
or SWITCH_STMT) or CONTINUE_STMT (not nested inside those except
perhaps SWITCH_STMT) and replace them with GOTO_EXPR to lazily created
label. */
static tree
expansion_stmt_find_bc_r (tree *tp, int *walk_subtrees, void *data)
{
tree t = *tp;
expansion_stmt_bc *bc_data = (expansion_stmt_bc *) data;
switch (TREE_CODE (t))
{
case WHILE_STMT:
*walk_subtrees = 0;
for (int i = 0; i < TREE_CODE_LENGTH (WHILE_STMT); ++i)
if (&TREE_OPERAND (t, i) != &WHILE_BODY (t))
cp_walk_tree (&TREE_OPERAND (t, i), expansion_stmt_find_bc_r,
data, bc_data->pset);
break;
case FOR_STMT:
*walk_subtrees = 0;
for (int i = 0; i < TREE_CODE_LENGTH (FOR_STMT); ++i)
if (&TREE_OPERAND (t, i) != &FOR_BODY (t))
cp_walk_tree (&TREE_OPERAND (t, i), expansion_stmt_find_bc_r,
data, bc_data->pset);
break;
case DO_STMT:
*walk_subtrees = 0;
for (int i = 0; i < TREE_CODE_LENGTH (DO_STMT); ++i)
if (&TREE_OPERAND (t, i) != &DO_BODY (t))
cp_walk_tree (&TREE_OPERAND (t, i), expansion_stmt_find_bc_r,
data, bc_data->pset);
break;
case TEMPLATE_FOR_STMT:
*walk_subtrees = 0;
for (int i = 0; i < TREE_CODE_LENGTH (TEMPLATE_FOR_STMT); ++i)
if (&TREE_OPERAND (t, i) != &TEMPLATE_FOR_BODY (t))
cp_walk_tree (&TREE_OPERAND (t, i), expansion_stmt_find_bc_r,
data, bc_data->pset);
break;
case SWITCH_STMT:
if (!bc_data->in_switch)
{
*walk_subtrees = 0;
for (int i = 0; i < TREE_CODE_LENGTH (SWITCH_STMT); ++i)
if (&TREE_OPERAND (t, i) != &SWITCH_STMT_BODY (t))
cp_walk_tree (&TREE_OPERAND (t, i), expansion_stmt_find_bc_r,
data, bc_data->pset);
bc_data->in_switch = true;
cp_walk_tree (&SWITCH_STMT_BODY (t), expansion_stmt_find_bc_r,
data, bc_data->pset);
bc_data->in_switch = false;
}
break;
case BREAK_STMT:
if (!bc_data->in_switch)
{
if (!bc_data->break_label)
{
bc_data->break_label = create_artificial_label (bc_data->loc);
TREE_USED (bc_data->break_label) = 1;
LABEL_DECL_BREAK (bc_data->break_label) = true;
}
*tp = build1_loc (EXPR_LOCATION (t), GOTO_EXPR, void_type_node,
bc_data->break_label);
}
break;
case CONTINUE_STMT:
if (!bc_data->continue_label)
{
bc_data->continue_label = create_artificial_label (bc_data->loc);
TREE_USED (bc_data->continue_label) = 1;
LABEL_DECL_CONTINUE (bc_data->continue_label) = true;
}
*tp = build1_loc (EXPR_LOCATION (t), GOTO_EXPR, void_type_node,
bc_data->continue_label);
break;
default:
if (TYPE_P (t))
*walk_subtrees = 0;
break;
}
return NULL_TREE;
}
/* Finish an expansion-statement. */
void
finish_expansion_stmt (tree expansion_stmt, tree args,
tsubst_flags_t complain, tree in_decl)
{
tree expansion_init = TEMPLATE_FOR_EXPR (expansion_stmt);
if (error_operand_p (expansion_init))
return;
enum expansion_stmt_kind {
esk_none,
esk_iterating,
esk_destructuring,
esk_enumerating
} kind = esk_none;
unsigned HOST_WIDE_INT n = 0;
tree range_decl = TEMPLATE_FOR_DECL (expansion_stmt);
bool is_decomp = false;
if (TREE_CODE (range_decl) == TREE_VEC)
{
is_decomp = true;
range_decl = TREE_VEC_ELT (range_decl, 0);
}
if (error_operand_p (range_decl))
return;
location_t loc = DECL_SOURCE_LOCATION (range_decl);
tree begin = NULL_TREE;
auto_vec<tree, 8> destruct_decls;
if (BRACE_ENCLOSED_INITIALIZER_P (expansion_init))
{
/* Enumerating expansion statements. */
kind = esk_enumerating;
n = CONSTRUCTOR_NELTS (expansion_init);
}
else if (TYPE_REF_P (TREE_TYPE (expansion_init))
? TREE_CODE (TREE_TYPE (TREE_TYPE (expansion_init))) != ARRAY_TYPE
: TREE_CODE (TREE_TYPE (expansion_init)) != ARRAY_TYPE)
{
tree range_temp, begin_expr, end_expr, iter_type;
range_temp = convert_from_reference (build_range_temp (expansion_init));
iter_type = cp_perform_range_for_lookup (range_temp, &begin_expr,
&end_expr, tf_none);
if (begin_expr != error_mark_node && end_expr != error_mark_node)
{
kind = esk_iterating;
gcc_assert (iter_type);
}
}
if (kind == esk_iterating)
{
/* Iterating expansion statements. */
tree end;
begin = cp_build_range_for_decls (loc, expansion_init, &end, true);
if (!error_operand_p (begin) && !error_operand_p (end))
{
tree i = get_target_expr (begin);
tree w = build_stmt (loc, WHILE_STMT, NULL_TREE, NULL_TREE,
NULL_TREE, NULL_TREE, NULL_TREE);
tree r = get_target_expr (build_zero_cst (ptrdiff_type_node));
tree iinc = build_x_unary_op (loc, PREINCREMENT_EXPR,
TARGET_EXPR_SLOT (i), NULL_TREE,
tf_warning_or_error);
tree rinc = build2 (PREINCREMENT_EXPR, ptrdiff_type_node,
TARGET_EXPR_SLOT (r),
build_int_cst (ptrdiff_type_node, 1));
WHILE_BODY (w) = build_compound_expr (loc, iinc, rinc);
WHILE_COND (w) = build_x_binary_op (loc, NE_EXPR, i, ERROR_MARK,
end, ERROR_MARK, NULL_TREE, NULL,
tf_warning_or_error);
tree e = build_compound_expr (loc, r, i);
e = build_compound_expr (loc, e, w);
e = build_compound_expr (loc, e, TARGET_EXPR_SLOT (r));
e = cxx_constant_value (e);
if (tree_fits_uhwi_p (e))
n = tree_to_uhwi (e);
}
}
else if (kind == esk_none)
{
kind = esk_destructuring;
HOST_WIDE_INT sz = cp_decomp_size (loc, TREE_TYPE (expansion_init),
tf_warning_or_error);
if (sz < 0)
return;
if (sz == 0)
{
error_at (loc, "empty structured binding");
return;
}
n = sz;
tree auto_node = make_auto ();
tree decomp_type = cp_build_reference_type (auto_node, true);
decomp_type = do_auto_deduction (decomp_type, expansion_init, auto_node);
tree decl = build_decl (loc, VAR_DECL, NULL_TREE, decomp_type);
TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_DECLARED_CONSTEXPR_P (decl)
= DECL_DECLARED_CONSTEXPR_P (range_decl);
if (DECL_DECLARED_CONSTEXPR_P (decl))
TREE_READONLY (decl) = 1;
fit_decomposition_lang_decl (decl, NULL_TREE);
pushdecl (decl);
cp_decomp this_decomp;
this_decomp.count = n;
destruct_decls.safe_grow (n, true);
for (unsigned HOST_WIDE_INT i = 0; i < n; ++i)
{
tree this_decl = build_decl (loc, VAR_DECL, NULL_TREE, make_auto ());
TREE_USED (this_decl) = 1;
DECL_ARTIFICIAL (this_decl) = 1;
DECL_DECLARED_CONSTEXPR_P (this_decl)
= DECL_DECLARED_CONSTEXPR_P (decl);
if (DECL_DECLARED_CONSTEXPR_P (decl))
TREE_READONLY (this_decl) = 1;
pushdecl (this_decl);
this_decomp.decl = this_decl;
destruct_decls[i] = this_decl;
}
DECL_NAME (decl) = for_range__identifier;
cp_finish_decl (decl, expansion_init,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING, &this_decomp);
DECL_NAME (decl) = NULL_TREE;
}
expansion_stmt_bc bc_data = { NULL_TREE, NULL_TREE, NULL, loc, false };
for (unsigned HOST_WIDE_INT i = 0; i < n; ++i)
{
tree scope = do_pushlevel (sk_block);
bool revert_outer
= (current_binding_level->level_chain
&& current_binding_level->level_chain->kind == sk_template_for);
/* Don't diagnose redeclaration of for-range-declaration decls.
The sk_template_for block is reused for the originally parsed
source as well as the lowered one. In the original one
redeclaration of the for-range-declaration decls in the substatement
should be diagnosed (i.e. declarations of the same name in sk_block
of the body vs. declarations in sk_template_for block). In the
lowered case, the sk_block added by do_pushlevel (sk_block) above
will be block in the lowering of each Si. Those blocks do redeclare
for-range-declaration, so temporarily change sk_template_for
kind to sk_block to avoid it being diagnosed as invalid. */
if (revert_outer)
current_binding_level->level_chain->kind = sk_block;
tree type = TREE_TYPE (range_decl);
if (args)
type = tsubst (type, args, complain | tf_tst_ok, in_decl);
tree decl = build_decl (loc, VAR_DECL, DECL_NAME (range_decl), type);
DECL_ATTRIBUTES (decl) = DECL_ATTRIBUTES (range_decl);
if (args)
apply_late_template_attributes (&decl, DECL_ATTRIBUTES (decl),
/*flags=*/0, args, complain,
in_decl);
DECL_DECLARED_CONSTEXPR_P (decl)
= DECL_DECLARED_CONSTEXPR_P (range_decl);
if (DECL_DECLARED_CONSTEXPR_P (decl))
TREE_READONLY (decl) = 1;
pushdecl (decl);
tree init = NULL_TREE;
switch (kind)
{
case esk_enumerating:
init = CONSTRUCTOR_ELT (expansion_init, i)->value;
break;
case esk_iterating:
tree iter_init, auto_node, iter_type, iter;
iter_init
= build_x_binary_op (loc, PLUS_EXPR, begin, ERROR_MARK,
build_int_cst (ptrdiff_type_node, i),
ERROR_MARK, NULL_TREE, NULL,
tf_warning_or_error);
auto_node = make_auto ();
iter_type = do_auto_deduction (auto_node, iter_init, auto_node);
iter = build_decl (loc, VAR_DECL, NULL_TREE, iter_type);
TREE_USED (iter) = 1;
DECL_ARTIFICIAL (iter) = 1;
TREE_STATIC (iter) = 1;
DECL_DECLARED_CONSTEXPR_P (iter) = 1;
pushdecl (iter);
cp_finish_decl (iter, iter_init, /*is_constant_init*/false,
NULL_TREE, LOOKUP_ONLYCONVERTING);
init = build_x_indirect_ref (loc, iter, RO_UNARY_STAR, NULL_TREE,
tf_warning_or_error);
break;
case esk_destructuring:
init = convert_from_reference (destruct_decls[i]);
break;
default:
gcc_unreachable ();
}
cp_decomp this_decomp = {};
if (is_decomp)
{
fit_decomposition_lang_decl (decl, NULL_TREE);
tree v = TEMPLATE_FOR_DECL (expansion_stmt);
this_decomp.count = TREE_VEC_LENGTH (v) - 1;
for (unsigned i = 0; i < this_decomp.count; ++i)
{
tree this_decl
= build_decl (loc, VAR_DECL,
DECL_NAME (TREE_VEC_ELT (v, i + 1)),
make_auto ());
TREE_USED (this_decl) = 1;
DECL_ARTIFICIAL (this_decl) = 1;
DECL_ATTRIBUTES (this_decl)
= DECL_ATTRIBUTES (TREE_VEC_ELT (v, i + 1));
if (DECL_PACK_P (TREE_VEC_ELT (v, i + 1)))
{
tree dtype = cxx_make_type (DECLTYPE_TYPE);
DECLTYPE_TYPE_EXPR (dtype) = this_decl;
DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (dtype) = 1;
SET_TYPE_STRUCTURAL_EQUALITY (dtype);
tree type = cxx_make_type (TYPE_PACK_EXPANSION);
PACK_EXPANSION_PATTERN (type) = dtype;
SET_TYPE_STRUCTURAL_EQUALITY (type);
PACK_EXPANSION_PARAMETER_PACKS (type) = this_decl;
TREE_TYPE (this_decl) = type;
}
if (args)
apply_late_template_attributes (&this_decl,
DECL_ATTRIBUTES (this_decl),
/*flags=*/0, args,
complain, in_decl);
DECL_DECLARED_CONSTEXPR_P (this_decl)
= DECL_DECLARED_CONSTEXPR_P (decl);
if (DECL_DECLARED_CONSTEXPR_P (decl))
TREE_READONLY (this_decl) = 1;
pushdecl (this_decl);
this_decomp.decl = this_decl;
}
}
cp_finish_decl (decl, init, false, NULL_TREE,
LOOKUP_ONLYCONVERTING, is_decomp ? &this_decomp : NULL);
if (revert_outer)
current_binding_level->level_chain->kind = sk_template_for;
tree targs = args;
if (args == NULL_TREE)
{
targs = make_tree_vec (1);
TREE_VEC_ELT (targs, 0) = build_int_cst (ptrdiff_type_node, i + 1);
}
if (args != NULL_TREE
|| push_tinst_level_loc (expansion_stmt, targs, loc))
{
local_specialization_stack lss (lss_copy);
register_local_specialization (decl, range_decl);
if (is_decomp)
{
tree d = this_decomp.decl;
unsigned int cnt = this_decomp.count;
tree v = TEMPLATE_FOR_DECL (expansion_stmt);
for (unsigned int i = 0; i < cnt; ++i, d = DECL_CHAIN (d))
register_local_specialization (d, TREE_VEC_ELT (v, cnt - i));
}
tsubst_stmt (TEMPLATE_FOR_BODY (expansion_stmt),
targs, complain, in_decl ? in_decl : range_decl);
if (args == NULL_TREE)
pop_tinst_level ();
}
tree stmt = do_poplevel (scope);
if (stmt)
{
add_stmt (stmt);
hash_set<tree> pset;
bc_data.continue_label = NULL_TREE;
bc_data.pset = &pset;
cp_walk_tree (&stmt, expansion_stmt_find_bc_r, &bc_data, &pset);
if (bc_data.continue_label)
add_stmt (build1 (LABEL_EXPR, void_type_node,
bc_data.continue_label));
}
}
if (bc_data.break_label)
add_stmt (build1 (LABEL_EXPR, void_type_node, bc_data.break_label));
if (args == NULL_TREE)
{
TREE_TYPE (range_decl) = error_mark_node;
if (DECL_HAS_VALUE_EXPR_P (range_decl))
{
SET_DECL_VALUE_EXPR (range_decl, NULL_TREE);
DECL_HAS_VALUE_EXPR_P (range_decl) = 0;
}
if (is_decomp)
{
tree v = TEMPLATE_FOR_DECL (expansion_stmt);
for (int i = 1; i < TREE_VEC_LENGTH (v); ++i)
{
tree d = TREE_VEC_ELT (v, i);
TREE_TYPE (d) = error_mark_node;
if (DECL_HAS_VALUE_EXPR_P (d))
{
SET_DECL_VALUE_EXPR (d, NULL_TREE);
DECL_HAS_VALUE_EXPR_P (d) = 0;
}
}
}
}
}
/* Set up the hash tables for template instantiations. */
void

View File

@ -677,7 +677,7 @@ do_poplevel (tree stmt_list)
/* Begin a new scope. */
static tree
tree
do_pushlevel (scope_kind sk)
{
tree ret = push_stmt_list ();
@ -1854,6 +1854,21 @@ finish_range_for_decl (tree range_for_stmt, tree decl, tree expr)
RANGE_FOR_BODY (range_for_stmt) = do_pushlevel (sk_block);
}
/* Begin the scope of an expansion-statement. */
tree
begin_template_for_scope (tree *init)
{
tree scope = do_pushlevel (sk_template_for);
if (processing_template_decl)
*init = push_stmt_list ();
else
*init = NULL_TREE;
return scope;
}
/* Finish a break-statement. */
tree

View File

@ -0,0 +1,16 @@
// PR c++/120776
// { dg-do compile { target c++11 } }
// { dg-options "" }
extern int b[];
void
foo (int n)
{
int a[n];
a[0] = 42;
auto [x] = a; // { dg-warning "structured bindings only available with" "" { target c++14_down } }
// { dg-error "cannot decompose variable length array 'int \\\[n\\\]'" "" { target *-*-* } .-1 }
auto [y] = b; // { dg-warning "structured bindings only available with" "" { target c++14_down } }
// { dg-error "deduced type 'int \\\[\\\]' for '<structured bindings>' is incomplete" "" { target *-*-* } .-1 }
}

View File

@ -0,0 +1,216 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++14 } }
// { dg-options "" }
template <typename T, typename U>
constexpr bool is_same_v = false;
template <typename T>
constexpr bool is_same_v<T, T> = true;
struct S { int a; long b; short c; };
struct T { long long a; unsigned b; signed char c; };
struct U { float a; double b; long double c; };
struct V { S l, m, n; T o; U p; };
constexpr S d = { 1, 2, 3 }, e = { 4, 5, 6 }, f = { 7, 8, 9 };
constexpr T j = { 10, 11, 12 };
U k = { 13.0f, 14.5, 15.5 }, m = { 7.0f, 7.0, 7.0 };
V l = { d, e, f, j, k };
struct A
{
int x;
constexpr explicit A (int v) : x(v) {}
constexpr A &operator ++ () { ++x; return *this; }
constexpr int operator * () { return x; }
constexpr bool operator != (const A &o) { return x != o.x; }
constexpr A operator + (int o) { A r (x + o); return r; }
};
struct C
{
int x, y, z;
constexpr explicit C (int u, int v, int w) : x(u), y(v), z(w) {}
constexpr C &operator ++ () { ++x; --y; ++z; return *this; }
constexpr C operator * () { return *this; }
constexpr bool operator != (const C &o) { return x != o.x || y != o.y || z != o.z; }
constexpr C operator + (int o) { C r (x + o, y - o, z + o); return r; }
};
namespace N
{
struct B { constexpr B () {} };
constexpr A begin (B &) { return A (0); }
constexpr A end (B &) { return A (6); }
}
namespace O
{
struct D { constexpr D () {} };
constexpr C begin (D &) { return C (0, 42, 5); }
constexpr C end (D &) { return C (6, 36, 11); }
}
long long
foo ()
{
long long r = 0;
template for (auto &g : { d, e, f, j, k }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += g.a + g.b + g.c;
decltype (g) s = g;
r += sizeof (s);
}
return r;
}
int
bar ()
{
int r = 0;
template for (auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += i;
static_assert (is_same_v <decltype (i), int>);
}
return r;
}
int
baz ()
{
int a[] = { 2, 4, 6, 8, 10 };
int r = 0, i = 0;
template for (const int &w : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
if (&w != &a[i++])
break;
r += w;
if (w == 6)
continue;
++r;
}
return r;
}
long long
qux ()
{
long long r = 0;
template for (const auto &i : l) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += i.a * i.b * i.c;
decltype (i.a) s = 0;
decltype (i.c) t = 0;
r += sizeof (s) + sizeof (t);
}
return r;
}
long long
corge ()
{
long long r = 0;
int z = 0;
template for (const auto &[g, h, i] : { d, e, f, j, m, k, k })// { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
++z;
if (z == 5)
continue;
++r;
if (z == 7)
break;
r += g + h + i;
decltype (h) s = 0;
r += sizeof (s) + sizeof (i);
}
return r;
}
int
garply ()
{
int r = 0;
template for (auto [g, h, i] : O::D {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += g + h + i; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
return r;
}
int
freddy ()
{
S a[] = { { 2, 4, 6 }, { 8, 10, 12 }, { 14, 16, 18 } };
int r = 0, i = 0;
template for (const auto &[u, v, w] : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
if (&u != &a[i].a || &v != &a[i].b || &w != &a[i].c)
break;
++i;
r += u + v + w;
if (w == 12)
continue;
++r;
}
return r;
}
long long
quux ()
{
long long r = 0;
template for (auto [i, j, k] : l) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += i * j * k; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
return r;
}
long long
boo ()
{
long long r = 0;
template for (auto g : { 1, 2U, 3LL, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
switch (g)
{
case 1:
r += 3;
break;
case 2:
r += 5;
break;
case 3:
r += 9;
break;
case 4:
r += 13;
break;
default:
__builtin_abort ();
}
}
return r;
}
int
main ()
{
if (foo () != 121 + 3 * sizeof (S) + sizeof (T) + sizeof (U))
__builtin_abort ();
if (bar () != 15)
__builtin_abort ();
if (baz () != 34)
__builtin_abort ();
if (qux () != (4871 + 3 * (sizeof (int) + sizeof (short))
+ sizeof (long long) + sizeof (signed char)
+ sizeof (float) + sizeof (long double)))
__builtin_abort ();
if (corge () != (127 + 3 * (sizeof (long) + sizeof (short))
+ sizeof (unsigned) + sizeof (signed char)
+ sizeof (double) + sizeof (long double)))
__builtin_abort ();
if (garply () != 297)
__builtin_abort ();
if (freddy () != 92)
__builtin_abort ();
if (quux () != 4871)
__builtin_abort ();
if (boo () != 30)
__builtin_abort ();
}

View File

@ -0,0 +1,17 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
// { dg-options "" }
int a;
long b;
void
foo ()
{
template for (auto g : { &a, &b, 2L, &a }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message "required from here" "" { target *-*-* } .-1 }
decltype (*g) h = *g; // { dg-error "invalid type argument of unary" }
}
}
// { dg-message "In instantiation of 'template for' iteration 3:" "" { target *-*-* } 0 }

View File

@ -0,0 +1,93 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
// { dg-options "" }
struct S { using type = S; int s; };
S a = { 1 }, b = { 2 };
constexpr S c[] = { { 3 }, { 4 }, { 5 }, { 6 }, { 7 } };
struct T { using type = T; int s; };
T d = { 8 };
struct U {
constexpr const S *begin () const { return &c[0]; }
constexpr const S *end () const { return &c[s]; }
int s;
};
struct V { int a; long b; double c; };
void
foo ()
{
template for (auto g : { a, b }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
template for (auto g : { d, b }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
static constexpr U u = { 3 };
template for (auto g : u) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g;
}
V v = { 9, 10L, 11.0 };
template for (auto g : v) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
}
template <int N>
void
bar ()
{
template for (auto g : { a, b }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
template for (auto g : { d, b }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
static constexpr U u = { 3 };
template for (auto g : u) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g;
}
V v = { 9, 10L, 11.0 };
template for (auto g : v) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
}
template <typename S, typename U, typename V>
void
baz ()
{
template for (auto g : { (S) a, b }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
template for (auto g : { d, (S) b }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
static constexpr U u = { 3 };
template for (auto g : u) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
V v = { 9, 10L, 11.0 };
template for (auto g : v) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g; // { dg-error "need 'typename' before 'decltype \\\(g\\\)::type' because 'decltype \\\(g\\\)' is a dependent scope" }
}
}
void
qux ()
{
bar <0> ();
baz <S, U, V> ();
}

View File

@ -0,0 +1,54 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++14 } }
// { dg-options "" }
constexpr int
foo (auto const &... x) // { dg-warning "use of 'auto' in parameter declaration only available with" "" { target c++17_down } }
{
int r = 0;
template for (auto const &c : {x...}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += c[0];
return r;
}
constexpr int c1[] = { 1, 2, 3 };
constexpr int c2[] = { 4, 3, 2, 1 };
static_assert (foo (c1, c2) == 5, "");
template <typename T, unsigned long N>
struct array
{
T e[N];
constexpr T *begin () noexcept { return &e[0]; }
constexpr const T *begin () const noexcept { return &e[0]; }
constexpr T *end () noexcept { return &e[N]; }
constexpr const T *end () const noexcept { return &e[N]; }
};
static constexpr array <int, 3> a { 1, 2, 3 };
constexpr int
bar ()
{
int r = 0;
template for (constexpr int s : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += sizeof (char[s]);
return r;
}
static_assert (bar () == 6, "");
struct S { int i; short s; };
constexpr long
baz (S s)
{
long r = 0;
template for (auto x : s) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += sizeof (x);
}
return r;
}
static_assert (baz (S {}) == sizeof (int) + sizeof (short), "");

View File

@ -0,0 +1,97 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++11 } }
// { dg-options "" }
namespace std {
template <typename T> struct tuple_size;
template <int, typename> struct tuple_element;
}
struct S { int s; };
constexpr S c[] = { { 3 }, { 4 }, { 5 }, { 6 }, { 7 } };
struct U {
constexpr const S *begin () const { return &c[0]; }
constexpr const S *end () const { return &c[s]; }
int s;
};
constexpr U u1 = { 3 }, u2 = { 0 };
struct V {
int i, j;
template <int I> int &get () { return i; }
};
template<> struct std::tuple_size <V> { static const int value = 2; };
template<int I> struct std::tuple_element <I, V> { using type = int; };
struct W {
int w;
W (int x) : w (x) {}
~W () {}
};
struct X {
V i, j;
template <int I> V &get () { return j; }
};
template<> struct std::tuple_size <X> { static const int value = 3; };
template<int I> struct std::tuple_element <I, X> { using type = V; };
long long
foo ()
{
long long r = 0;
template for (auto h = 2; constexpr auto g : u1) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += g.s + h;
template for (long long h = ++r; auto g : u2) // { dg-warning "'template for' only available with" "" { target c++23_down } }
__builtin_abort ();
return r;
}
long long
bar ()
{
long long r = 0;
template for (W w { 42 }; auto i : V { 42, 10 }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += i + (w.w == 42);
return r;
}
long long
baz ()
{
long long r = 0;
template for (constexpr auto x = 5; auto [ i, j ] : X { { 5, 6 }, { 7, 8 } }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += i + j + (x == 5); // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
return r;
}
V &&
qux (V &&x) noexcept
{
return static_cast<V &&> (x);
}
long long
freddy ()
{
long long r = 0;
V a { 1, 2 }, b { 3, 4 };
template for (auto i : { qux (static_cast<V &&> (a)), qux (static_cast<V &&> (b)) }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += i.i + i.j;
return r;
}
int
main ()
{
if (foo () != 19)
__builtin_abort ();
if (bar () != 86)
__builtin_abort ();
if (baz () != 45)
__builtin_abort ();
if (freddy () != 10)
__builtin_abort ();
}

View File

@ -0,0 +1,75 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++11 } }
// { dg-options "" }
struct S { int s; };
constexpr S c[] = { { 3 }, { 4 }, { 5 }, { 6 }, { 7 } };
struct U {
constexpr const S *begin () const { return &c[0]; }
constexpr const S *end () const { return &c[s]; }
int s;
};
constexpr U u1 = { 3 }, u2 = { 0 };
struct V {
constexpr V () : a (1), b (2), c (3.0) {}
constexpr int foo () const { return a; }
constexpr unsigned long bar () const { return b; }
constexpr double baz () const { return c; }
int a;
unsigned long b;
double c;
};
long long
foo ()
{
long long r = 0;
template for (constexpr auto h = 2; constexpr auto g : u1) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
constexpr auto i = g.s + h;
r += i;
}
template for (constexpr auto h = 42; constexpr auto g : u2) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
constexpr auto i = g.s + h;
__builtin_abort ();
}
return r;
}
long long
bar ()
{
long long r = 0;
template for (constexpr S a { 42 }; constexpr auto b : { S { 1 }, S { 3 }, S { 5 } }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
constexpr auto c = a.s + b.s;
r += c;
}
return r;
}
constexpr V v;
long long
baz ()
{
long long r = 0;
template for (constexpr auto x = 5; constexpr auto y : v) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
constexpr auto z = x + y;
r += z;
}
return r;
}
int
main ()
{
if (foo () != 18)
__builtin_abort ();
if (bar () != 135)
__builtin_abort ();
if (baz () != 21)
__builtin_abort ();
}

View File

@ -0,0 +1,47 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
// { dg-options "" }
namespace std {
template <typename T>
struct initializer_list {
private:
T *a;
decltype (sizeof 0) b;
public:
constexpr decltype (sizeof 0) size () const noexcept { return b; }
constexpr const T *begin () const noexcept { return a; }
constexpr const T *end () const noexcept { return begin () + size (); }
};
}
struct A {};
struct B { int b; B () : b (42) {} };
struct C : public B { int c; C () : c (42), B () {} };
extern int f[];
void
foo (int n)
{
int c[0] = {}, d[n];
int e = 42;
d[0] = 42;
template for (auto a : A {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "cannot decompose class type 'A' without non-static data members" "" { target *-*-* } .-1 }
template for (int b : B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
;
template for (int i : c) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "empty structured binding" "" { target *-*-* } .-1 }
template for (int i : d) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "cannot decompose variable length array" "" { target *-*-* } .-1 }
template for (auto a : C {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "cannot decompose class type 'C': both it and its base class 'B' have non-static data members" "" { target *-*-* } .-1 }
template for (auto a : e) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "cannot decompose non-array non-class type 'int'" "" { target *-*-* } .-1 }
template for (auto a : { .id1 = 5, .id2 = 6LL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "designators in 'template for' initializer" "" { target *-*-* } .-1 }
template for (auto a : { .id3 { 5 }, .id4 = { 1.0 } }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "designators in 'template for' initializer" "" { target *-*-* } .-1 }
template for (int i : f) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "cannot decompose array of unknown bound 'int \\\[\\\]'" "" { target *-*-* } .-1 }
}

View File

@ -0,0 +1,68 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
// { dg-options "" }
struct A { int s; };
constexpr A a[] = { { 3 }, { 4 }, { 5 }, { 6 }, { 7 } };
struct B {
constexpr const A *begin () const { return &a[0]; }
constexpr const A *end () const { return &a[s]; }
int s;
};
constexpr B b = { 3 };
struct C {
C (int x) : s (x) {}
constexpr const A *begin () const { return &a[0]; }
constexpr const A *end () const { return &a[s]; }
int s;
};
struct D {
constexpr D (int x) : s (x) {}
constexpr const A *begin () const { return &a[0]; }
const A *end () const { return &a[s]; }
int s;
};
struct E {
constexpr E (int x) : s (x) {}
const A *begin () const { return &a[0]; }
constexpr const A *end () const { return &a[s]; }
int s;
};
struct F { F () : s (42) {} F (int x) : s (x) {} int s; };
F g[] = { { 3 }, { 4 }, { 5 }, { 6 }, { 7 } };
struct G {
constexpr G (int x) : s (x) {}
constexpr F *begin () const { return &g[0]; }
constexpr F *end () const { return &g[s]; }
int s;
};
struct H { int a; F b; int c; };
void
foo ()
{
B c = { 3 };
template for (constexpr auto g : c) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "'c' is not a constant expression" "" { target *-*-* } .-1 }
C d = { 3 };
template for (constexpr auto g : d) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "'d' is not a constant expression" "" { target *-*-* } .-1 }
// { dg-error "call to non-'constexpr' function 'const A\\\* C::begin\\\(\\\) const'" "" { target c++11_down } .-1 }
// { dg-error "call to non-'constexpr' function 'const A\\\* C::end\\\(\\\) const'" "" { target c++11_down } .-2 }
constexpr D e = { 3 };
template for (constexpr auto g : e) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "'e' is not a constant expression" "" { target *-*-* } .-1 }
// { dg-error "call to non-'constexpr' function 'const A\\\* D::end\\\(\\\) const'" "" { target *-*-* } .-1 }
constexpr E f = { 3 };
template for (constexpr auto g : f) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "'f' is not a constant expression" "" { target *-*-* } .-1 }
// { dg-error "call to non-'constexpr' function 'const A\\\* E::begin\\\(\\\) const'" "" { target *-*-* } .-1 }
constexpr G h = { 3 };
template for (constexpr auto g : h) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "'h' is not a constant expression" "" { target *-*-* } .-1 }
template for (constexpr auto g : { 1, 2, F { 3 }, 4L }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "the type 'const F' of 'constexpr' variable 'g' is not literal" "" { target *-*-* } .-1 }
template for (constexpr auto g : H {})// { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "the type 'const F' of 'constexpr' variable 'g' is not literal" "" { target *-*-* } .-1 }
// { dg-error "call to non-'constexpr' function 'F::F\\\(\\\)'" "" { target *-*-* } .-2 }
}

View File

@ -0,0 +1,37 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++11 } }
// { dg-options "" }
// { dg-additional-options "-frange-for-ext-temps" { target c++20_down } }
int c;
struct A {
A () { ++c; }
~A () { --c; }
A (int x) : a (x) { ++c; }
A (const A &x) : a (x.a) { ++c; }
int a;
};
struct B { int a; long b; double c; short d; };
B &
foo (A x, A y, A z)
{
static B r = { 1, 2, 42, 3 };
return r;
}
int
main ()
{
int r = 0;
if (c != 0)
__builtin_abort ();
template for (auto a : foo (A { 1 }, A { 2 }, A { 42 })) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
if (c != 3)
__builtin_abort ();
r += a;
}
if (c != 0 || r != 48)
__builtin_abort ();
}

View File

@ -0,0 +1,58 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++14 } }
// { dg-options "" }
struct S { int a; long b; short c; };
struct A
{
int x;
constexpr explicit A (int v) : x(v) {}
constexpr A &operator ++ () { ++x; return *this; }
constexpr int operator * () { return x; }
constexpr bool operator != (const A &o) { return x != o.x; }
constexpr A operator + (int o) { A r (x + o); return r; }
};
namespace N
{
struct B { constexpr B () {} };
constexpr A begin (B &) { return A (0); }
constexpr A end (B &) { return A (6); }
}
void
foo ()
{
template for (int a = 1; auto a : { 1, 2L }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "conflicting declaration 'auto a'" "" { target *-*-* } .-1 }
template for (int b = 1; auto c : { 1, 2L }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
;
int b = 1;
int c = 2;
template for (int d = 1; auto d : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "conflicting declaration 'auto d'" "" { target *-*-* } .-1 }
template for (int e = 1; auto e : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "conflicting declaration 'auto e'" "" { target *-*-* } .-1 }
template for (int f = 1; auto f : S { 1, 2, 3}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "conflicting declaration 'auto f'" "" { target *-*-* } .-1 }
template for (auto g : { 1, 2LL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
int g = 5; // { dg-error "conflicting declaration 'int g'" }
// { dg-error "redeclaration of 'int g'" "" { target *-*-* } .-1 }
template for (auto h : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
int h = 6; // { dg-error "redeclaration of 'int h'" }
template for (auto i : S { 1, 2, 3}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
int i = 7; // { dg-error "conflicting declaration 'int i'" }
// { dg-error "redeclaration of 'int i'" "" { target *-*-* } .-1 }
template for (auto j : { 1, 2LL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
int j = 5; // { dg-error "conflicting declaration 'int j'" }
} // { dg-error "redeclaration of 'int j'" "" { target *-*-* } .-1 }
template for (auto k : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
int k = 6; // { dg-error "redeclaration of 'int k'" }
}
template for (auto l : S { 1, 2, 3}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
int l = 7; // { dg-error "conflicting declaration 'int l'" }
} // { dg-error "redeclaration of 'int l'" "" { target *-*-* } .-1 }
}

View File

@ -0,0 +1,94 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++11 } }
// { dg-options "" }
namespace std {
template <typename T> struct tuple_size;
template <int, typename> struct tuple_element;
}
struct V {
int i, j;
template <int I> int &get () { return i; }
};
template<> struct std::tuple_size <V> { static const int value = 5; };
template<int I> struct std::tuple_element <I, V> { using type = int; };
constexpr V c[] = { { 3, 4 }, { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 } };
struct U {
constexpr const V *begin () const { return &c[0]; }
constexpr const V *end () const { return &c[s]; }
int s;
};
constexpr U u1 = { 3 }, u2 = { 0 };
struct W {
int w;
W (int x) : w (x) {}
~W () {}
};
struct X {
V i, j;
template <int I> V &get () { return j; }
};
template<> struct std::tuple_size <X> { static const int value = 3; };
template<int I> struct std::tuple_element <I, X> { using type = V; };
template <int N>
long long
foo ()
{
long long r = 0;
template for (auto h = 2; auto [_, ...i, _] : u1) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
if (sizeof... (i) != 3) // { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } .-2 }
__builtin_abort (); // { dg-warning "name-independent declarations only available with" "" { target c++23_down } .-3 }
r += i...[1] + h; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
}
template for (long long h = ++r; auto [...i, j] : u2) // { dg-warning "'template for' only available with" "" { target c++23_down } }
__builtin_abort (); // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } .-2 }
return r;
}
template <int N>
long long
bar ()
{
long long r = 0;
template for (W w { 42 }; auto [...i, j] : { V { 42, 10 }, V { 15, 26 }, V { 93, 12 } }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
if (sizeof... (i) != 4) // { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } .-2 }
__builtin_abort ();
r += i...[3] + (w.w == 42); // { dg-warning "pack indexing only available with" "" { target c++23_down } }
}
return r;
}
template <int N>
long long
baz ()
{
long long r = 0;
template for (constexpr auto x = 5; auto [ ...j ] : X { { 5, 6 }, { 7, 8 } }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
if (sizeof... (j) != 5) // { dg-warning "structured binding packs only available with" "" { target { c++17 && c++23_down } } .-2 }
__builtin_abort ();
r += j...[4] + j...[0] + (x == 5); // { dg-warning "pack indexing only available with" "" { target c++23_down } }
}
return r;
}
int
main ()
{
if (foo <0> () != 19)
__builtin_abort ();
if (bar <1> () != 153)
__builtin_abort ();
if (baz <2> () != 45)
__builtin_abort ();
}

View File

@ -0,0 +1,208 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++17 } }
// { dg-options "" }
template <typename T, typename U>
constexpr bool is_same_v = false;
template <typename T>
constexpr bool is_same_v<T, T> = true;
struct S { int a; long b; short c; };
struct T { long long a; unsigned b; signed char c; };
struct U { float a; double b; long double c; };
struct V { S l, m, n; T o; U p; };
constexpr S d = { 1, 2, 3 }, e = { 4, 5, 6 }, f = { 7, 8, 9 };
constexpr T j = { 10, 11, 12 };
U k = { 13.0f, 14.5, 15.5 }, m = { 7.0f, 7.0, 7.0 };
V l = { d, e, f, j, k };
struct A
{
int x;
constexpr explicit A (int v) : x(v) {}
constexpr A &operator ++ () { ++x; return *this; }
constexpr int operator * () { return x; }
constexpr bool operator != (const A &o) { return x != o.x; }
constexpr A operator + (int o) { A r (x + o); return r; }
};
struct C
{
int x, y, z;
constexpr explicit C (int u, int v, int w) : x(u), y(v), z(w) {}
constexpr C &operator ++ () { ++x; --y; ++z; return *this; }
constexpr C operator * () { return *this; }
constexpr bool operator != (const C &o) { return x != o.x || y != o.y || z != o.z; }
constexpr C operator + (int o) { C r (x + o, y - o, z + o); return r; }
};
namespace N
{
struct B { constexpr B () {} };
constexpr A begin (B &) { return A (0); }
constexpr A end (B &) { return A (6); }
}
namespace O
{
struct D { constexpr D () {} };
constexpr C begin (D &) { return C (0, 42, 5); }
constexpr C end (D &) { return C (6, 36, 11); }
}
#if __cpp_nontype_template_parameter_class >= 201806L
template <auto ... Z>
long long
foo ()
{
long long r = 0;
template for (const auto &g : { Z... }) // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } }
{
r += g.a + g.b + g.c;
decltype (g) s = g;
r += sizeof (s);
}
return r;
}
#endif
template <typename T, int N>
int
bar ()
{
int r = 0;
template for (auto i : T {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += i + N;
static_assert (is_same_v <decltype (i), int>);
}
return r;
}
template <typename T>
int
baz ()
{
T a[] = { 2, 4, 6, 8, 10 };
int r = 0, i = 0;
template for (const int &w : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
if (&w != &a[i++])
break;
r += w;
if (w == 6)
continue;
++r;
}
return r;
}
template <typename T>
long long
qux ()
{
T l = { d, e, f, j, k };
long long r = 0;
template for (const auto &i : l) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += i.a * i.b * i.c;
decltype (i.a) s = 0;
decltype (i.c) t = 0;
r += sizeof (s) + sizeof (t);
}
return r;
}
template <typename T>
long long
corge ()
{
long long r = 0;
int z = 0;
template for (const auto &[g, h, i] : { d, e, f, j, (T) m, k, k })// { dg-warning "'template for' only available with" "" { target c++23_down } }
{
++z;
if (z == 5)
continue;
++r;
if (z == 7)
break;
r += g + h + i;
decltype (h) s = 0;
r += sizeof (s) + sizeof (i);
}
return r;
}
template <typename T>
int
garply ()
{
int r = 0;
template for (auto [g, h, i] : T {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += g + h + i;
return r;
}
template <typename T>
int
freddy ()
{
T a[] = { { 2, 4, 6 }, { 8, 10, 12 }, { 14, 16, 18 } };
int r = 0, i = 0;
template for (const auto &[u, v, w] : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
if (&u != &a[i].a || &v != &a[i].b || &w != &a[i].c)
break;
++i;
r += u + v + w;
if (w == 12)
continue;
++r;
}
return r;
}
template <typename T, typename U, int N>
long long
quux ()
{
T l = { d, e, f, j, k };
long long r = 0;
template for (auto [i, j, k] : l) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
U u = -1;
r += i * j * k + u + N;
}
return r;
}
int
main ()
{
#if __cpp_nontype_template_parameter_class >= 201806L
if (foo <d, e, f, j, U { 13.0f, 14.5, 15.5 }> () != 121 + 3 * sizeof (S) + sizeof (T) + sizeof (U))
__builtin_abort ();
#endif
if (bar <N::B, 0> () != 15)
__builtin_abort ();
if (bar <N::B, 1> () != 21)
__builtin_abort ();
if (baz <int> () != 34)
__builtin_abort ();
if (qux <V> () != (4871 + 3 * (sizeof (int) + sizeof (short))
+ sizeof (long long) + sizeof (signed char)
+ sizeof (float) + sizeof (long double)))
__builtin_abort ();
if (corge <U> () != (127 + 3 * (sizeof (long) + sizeof (short))
+ sizeof (unsigned) + sizeof (signed char)
+ sizeof (double) + sizeof (long double)))
__builtin_abort ();
if (garply <O::D> () != 297)
__builtin_abort ();
if (freddy <S> () != 92)
__builtin_abort ();
if (quux <V, unsigned char, 1> () != 4876L + 5L * (unsigned char) -1)
__builtin_abort ();
if (quux <V, int, 3> () != 4881)
__builtin_abort ();
}

View File

@ -0,0 +1,197 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++14 } }
// { dg-options "" }
template <typename T, typename U>
constexpr bool is_same_v = false;
template <typename T>
constexpr bool is_same_v<T, T> = true;
struct S { int a; long b; short c; };
struct T { long long a; unsigned b; signed char c; };
struct U { float a; double b; long double c; };
struct V { S l, m, n; T o; U p; };
constexpr S d = { 1, 2, 3 }, e = { 4, 5, 6 }, f = { 7, 8, 9 };
constexpr T j = { 10, 11, 12 };
U k = { 13.0f, 14.5, 15.5 }, m = { 7.0f, 7.0, 7.0 };
V l = { d, e, f, j, k };
struct A
{
int x;
constexpr explicit A (int v) : x(v) {}
constexpr A &operator ++ () { ++x; return *this; }
constexpr int operator * () { return x; }
constexpr bool operator != (const A &o) { return x != o.x; }
constexpr A operator + (int o) { A r (x + o); return r; }
};
struct C
{
int x, y, z;
constexpr explicit C (int u, int v, int w) : x(u), y(v), z(w) {}
constexpr C &operator ++ () { ++x; --y; ++z; return *this; }
constexpr C operator * () { return *this; }
constexpr bool operator != (const C &o) { return x != o.x || y != o.y || z != o.z; }
constexpr C operator + (int o) { C r (x + o, y - o, z + o); return r; }
};
namespace N
{
struct B { constexpr B () {} };
constexpr A begin (B &) { return A (0); }
constexpr A end (B &) { return A (6); }
}
namespace O
{
struct D { constexpr D () {} };
constexpr C begin (D &) { return C (0, 42, 5); }
constexpr C end (D &) { return C (6, 36, 11); }
}
template <int N>
long long
foo ()
{
long long r = 0;
template for (auto &g : { d, e, f, j, k }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += g.a + g.b + g.c;
decltype (g) s = g;
r += sizeof (s) + N;
}
return r;
}
template <typename T>
int
bar ()
{
int r = 0;
template for (auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += i;
static_assert (is_same_v <decltype (i), T>);
}
return r;
}
template <int N>
int
baz ()
{
int a[] = { 2, 4, N, 8, 10 };
int r = 0, i = 0;
template for (const int &w : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
if (&w != &a[i++])
break;
r += w;
if (w == N)
continue;
++r;
}
return r;
}
template <int N>
long long
qux ()
{
long long r = 0;
template for (const auto &i : l) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += i.a * i.b * i.c;
decltype (i.a) s = N;
decltype (i.c) t = 0;
r += sizeof (s) + sizeof (t);
}
return r;
}
template <long long N>
long long
corge ()
{
long long r = N;
int z = 0;
template for (const auto &[g, h, i] : { d, e, f, j, m, k, k })// { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
++z;
if (z == 5)
continue;
++r;
if (z == 7)
break;
r += g + h + i;
decltype (h) s = 0;
r += sizeof (s) + sizeof (i);
}
return r;
}
template <typename T>
int
garply ()
{
int r = 0;
template for (auto [g, h, i] : O::D {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += g + h + i + (T) 0; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
return r;
}
template <int N>
int
freddy ()
{
S a[] = { { 2, 4, 6 }, { 8, N, 12 }, { 14, 16, 18 } };
int r = 0, i = 0;
template for (const auto &[u, v, w] : a) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
if (&u != &a[i].a || &v != &a[i].b || &w != &a[i].c)
break;
++i;
r += u + v + w;
if (w == 12)
continue;
++r;
}
return r;
}
template <long long N>
long long
quux ()
{
long long r = N;
template for (auto [i, j, k] : l) // { dg-warning "'template for' only available with" "" { target c++23_down } }
r += i * j * k; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
return r;
}
int
main ()
{
if (foo <0> () != 121 + 3 * sizeof (S) + sizeof (T) + sizeof (U))
__builtin_abort ();
if (foo <42> () != 121 + 3 * sizeof (S) + sizeof (T) + sizeof (U) + 5 * 42)
__builtin_abort ();
if (bar <int> () != 15)
__builtin_abort ();
if (baz <6> () != 34)
__builtin_abort ();
if (qux <0> () != (4871 + 3 * (sizeof (int) + sizeof (short))
+ sizeof (long long) + sizeof (signed char)
+ sizeof (float) + sizeof (long double)))
__builtin_abort ();
if (corge <0> () != (127 + 3 * (sizeof (long) + sizeof (short))
+ sizeof (unsigned) + sizeof (signed char)
+ sizeof (double) + sizeof (long double)))
__builtin_abort ();
if (garply <long long> () != 297)
__builtin_abort ();
if (freddy <10> () != 92)
__builtin_abort ();
if (quux <0> () != 4871)
__builtin_abort ();
}

View File

@ -0,0 +1,35 @@
// C++26 P1306R5 - Expansion statements
// { dg-do run { target c++14 } }
// { dg-options "" }
struct S { int a; long b; short c; };
struct T { long long a; unsigned b; signed char c; };
struct U { float a; double b; long double c; };
constexpr S d = { 1, 2, 3 }, f = { 7, 8, 9 };
constexpr T j = { 10, 11, 12 };
constexpr U k = { 13.0f, 14.5, 15.5 };
template <typename T>
long long
foo ()
{
auto s = [] (auto f)
{
long long r = 0;
template for (auto g : { d, T { 4, 5, 6 }, f, j, k }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
r += g.a + g.b + g.c;
decltype (g) s = g;
r += sizeof (s);
}
return r;
};
return s (f);
}
int
main ()
{
if (foo <S> () != 121 + 3 * sizeof (S) + sizeof (T) + sizeof (U))
__builtin_abort ();
}

View File

@ -0,0 +1,96 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++14 } }
// { dg-options "" }
void
foo (int x)
{
switch (x)
{
case 1:
template for (auto g : { 1, 2U, 3LL, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
case 2: // { dg-error "jump to case label" }
break;
}
case 3:
template for (auto g : { 1, 2U, 3LL, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
default: // { dg-error "jump to case label" }
break;
}
}
template for (auto g : { 1, 2U, 3LL, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
lab1:; // { dg-error "identifier label 'lab1' in 'template for' body" }
}
switch (x)
{
case 1:
template for (auto g : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
case 2: // { dg-error "jump to case label" }
break;
}
case 3:
template for (auto g : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
default: // { dg-error "jump to case label" }
break;
}
}
template for (auto g : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
lab2:; // { dg-error "identifier label 'lab2' in 'template for' body" }
}
}
template <typename T, T N>
void
bar (int x)
{
switch (x)
{
case 1:
template for (auto g : { 1, N, 3LL, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
case 2: // { dg-error "jump to case label" }
break;
}
case 3:
template for (auto g : { 1, N, 3LL, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
default: // { dg-error "jump to case label" }
break;
}
}
template for (auto g : { 1, 2U, N, 4ULL }) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
lab1:; // { dg-error "identifier label 'lab1' in 'template for' body" }
}
switch (x)
{
case 1:
template for (auto g : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
case 2: // { dg-error "jump to case label" }
break;
}
case 3:
template for (auto g : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{ // { dg-message " enters 'template for' statement" "" { target *-*-* } .-1 }
default: // { dg-error "jump to case label" }
break;
}
}
template for (auto g : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
lab2:; // { dg-error "identifier label 'lab2' in 'template for' body" }
}
}
void
baz (int x)
{
bar <unsigned, 2U> (x);
}

View File

@ -0,0 +1,88 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
int z[3];
void
foo ()
{
template for (static auto a : {}) // { dg-error "for-range-declaration cannot be 'static'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (thread_local auto a : {}) // { dg-error "for-range-declaration cannot be 'thread_local'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (__thread auto a : {}) // { dg-error "for-range-declaration cannot be '__thread'" }
; // { dg-error "function-scope 'a' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (register auto a : {}) // { dg-error "for-range-declaration cannot be 'register'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (extern auto a : {}) // { dg-error "for-range-declaration cannot be 'extern'" }
; // { dg-error "'a' has both 'extern' and initializer" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto a : {}) // { dg-error "non-member 'a' cannot be declared 'mutable'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (virtual auto a : {}) // { dg-error "'virtual' outside class declaration" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (explicit auto a : {}) // { dg-error "'explicit' outside class declaration" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (friend auto a : {}) // { dg-error "'friend' used outside of class" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (typedef auto a : {}) // { dg-error "typedef declared 'auto'" }
; // { dg-error "typedef 'a' is initialized \\\(use 'decltype' instead\\\)" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto a : {}) // { dg-error "a variable cannot be declared 'consteval'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto a : {}) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto a : {}) // { dg-error "'inline' specifier invalid for variable 'a' declared at block scope" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (struct S { int a; } a : {}) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (enum E { E0 } a : {}) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
}
void
bar ()
{
template for (static auto a : z) // { dg-error "for-range-declaration cannot be 'static'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (thread_local auto a : z) // { dg-error "for-range-declaration cannot be 'thread_local'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (__thread auto a : z) // { dg-error "for-range-declaration cannot be '__thread'" }
; // { dg-error "function-scope 'a' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (register auto a : z) // { dg-error "for-range-declaration cannot be 'register'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (extern auto a : z) // { dg-error "for-range-declaration cannot be 'extern'" }
; // { dg-error "'a' has both 'extern' and initializer" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto a : z) // { dg-error "non-member 'a' cannot be declared 'mutable'" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (virtual auto a : z) // { dg-error "'virtual' outside class declaration" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (explicit auto a : z) // { dg-error "'explicit' outside class declaration" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (friend auto a : z) // { dg-error "'friend' used outside of class" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (typedef auto a : z) // { dg-error "typedef declared 'auto'" }
; // { dg-error "typedef 'a' is initialized \\\(use 'decltype' instead\\\)" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto a : z) // { dg-error "a variable cannot be declared 'consteval'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto a : z) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto a : z) // { dg-error "'inline' specifier invalid for variable 'a' declared at block scope" }
; // { dg-error "'template for' only available with" "" { target c++23_down } .-1 }
template for (struct S { int a; } a : z) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-error "conversion from 'int' to non-scalar type 'bar\\\(\\\)::S' requested" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
// { dg-error "conversion from 'int' to non-scalar type 'bar\\\(\\\)::S' requested" "" { target *-*-* } .-2 }
template for (enum E { E0 } a : z) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-error "invalid conversion from 'int' to 'bar\\\(\\\)::E'" "" { target *-*-* } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
// { dg-error "invalid conversion from 'int' to 'bar\\\(\\\)::E'" "" { target *-*-* } .-2 }
}

View File

@ -0,0 +1,89 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
// { dg-options "" }
int z[3];
void
foo ()
{
template for (static auto a : {}) // { dg-warning "for-range-declaration cannot be 'static'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (thread_local auto a : {}) // { dg-warning "for-range-declaration cannot be 'thread_local'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (__thread auto a : {}) // { dg-warning "for-range-declaration cannot be '__thread'" }
; // { dg-warning "function-scope 'a' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (register auto a : {}) // { dg-warning "for-range-declaration cannot be 'register'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (extern auto a : {}) // { dg-warning "for-range-declaration cannot be 'extern'" }
; // { dg-error "'a' has both 'extern' and initializer" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto a : {}) // { dg-error "non-member 'a' cannot be declared 'mutable'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (virtual auto a : {}) // { dg-error "'virtual' outside class declaration" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (explicit auto a : {}) // { dg-error "'explicit' outside class declaration" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (friend auto a : {}) // { dg-error "'friend' used outside of class" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (typedef auto a : {}) // { dg-error "typedef declared 'auto'" }
; // { dg-error "typedef 'a' is initialized \\\(use 'decltype' instead\\\)" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto a : {}) // { dg-error "a variable cannot be declared 'consteval'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto a : {}) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto a : {}) // { dg-error "'inline' specifier invalid for variable 'a' declared at block scope" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (struct S { int a; } a : {}) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (enum E { E0 } a : {}) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
}
void
bar ()
{
template for (static auto a : z) // { dg-warning "for-range-declaration cannot be 'static'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (thread_local auto a : z) // { dg-warning "for-range-declaration cannot be 'thread_local'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (__thread auto a : z) // { dg-warning "for-range-declaration cannot be '__thread'" }
; // { dg-warning "function-scope 'a' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (register auto a : z) // { dg-warning "for-range-declaration cannot be 'register'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (extern auto a : z) // { dg-warning "for-range-declaration cannot be 'extern'" }
; // { dg-error "'a' has both 'extern' and initializer" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto a : z) // { dg-error "non-member 'a' cannot be declared 'mutable'" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (virtual auto a : z) // { dg-error "'virtual' outside class declaration" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (explicit auto a : z) // { dg-error "'explicit' outside class declaration" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (friend auto a : z) // { dg-error "'friend' used outside of class" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (typedef auto a : z) // { dg-error "typedef declared 'auto'" }
; // { dg-error "typedef 'a' is initialized \\\(use 'decltype' instead\\\)" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto a : z) // { dg-error "a variable cannot be declared 'consteval'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto a : z) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto a : z) // { dg-error "'inline' specifier invalid for variable 'a' declared at block scope" }
; // { dg-warning "'template for' only available with" "" { target c++23_down } .-1 }
template for (struct S { int a; } a : z) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-error "conversion from 'int' to non-scalar type 'bar\\\(\\\)::S' requested" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
// { dg-error "conversion from 'int' to non-scalar type 'bar\\\(\\\)::S' requested" "" { target *-*-* } .-2 }
template for (enum E { E0 } a : z) // { dg-error "types may not be defined in a for-range-declaration" }
; // { dg-error "invalid conversion from 'int' to 'bar\\\(\\\)::E'" "" { target *-*-* } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
// { dg-error "invalid conversion from 'int' to 'bar\\\(\\\)::E'" "" { target *-*-* } .-2 }
}

View File

@ -0,0 +1,109 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
struct S { int y; } z[3];
template <int N>
void
foo ()
{
template for (static auto [ a ] : {}) // { dg-error "for-range-declaration cannot be 'static'" }
; // { dg-error "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
// { dg-error "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-3 }
template for (thread_local auto [ a ] : {}) // { dg-error "for-range-declaration cannot be 'thread_local'" }
; // { dg-error "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 }
// { dg-error "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-3 }
template for (__thread auto [ a ] : {}) // { dg-error "for-range-declaration cannot be '__thread'" }
; // { dg-error "function-scope 'structured binding' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-error "structured binding declaration can be '__thread' only in" "" { target c++17_down } .-2 }
// { dg-error "structured bindings only available with" "" { target c++14_down } .-3 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-4 }
template for (register auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'register'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (extern auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'extern'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'mutable'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (virtual auto [ a ] : {}) // { dg-error "'virtual' outside class declaration" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (explicit auto [ a ] : {}) // { dg-error "'explicit' outside class declaration" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (friend auto [ a ] : {}) // { dg-error "'friend' used outside of class" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (typedef auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'typedef'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'consteval'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto [ a ] : {}) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'inline'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
}
template <int N>
void
bar ()
{
template for (static auto [ a ] : z) // { dg-error "for-range-declaration cannot be 'static'" }
; // { dg-error "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
// { dg-error "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-3 }
template for (thread_local auto [ a ] : z) // { dg-error "for-range-declaration cannot be 'thread_local'" }
; // { dg-error "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 }
// { dg-error "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-3 }
template for (__thread auto [ a ] : z) // { dg-error "for-range-declaration cannot be '__thread'" }
; // { dg-error "function-scope 'structured binding' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-error "structured binding declaration can be '__thread' only in" "" { target c++17_down } .-2 }
// { dg-error "structured bindings only available with" "" { target c++14_down } .-3 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-4 }
template for (register auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'register'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (extern auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'extern'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'mutable'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (virtual auto [ a ] : z) // { dg-error "'virtual' outside class declaration" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (explicit auto [ a ] : z) // { dg-error "'explicit' outside class declaration" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (friend auto [ a ] : z) // { dg-error "'friend' used outside of class" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
template for (typedef auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'typedef'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'consteval'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto [ a ] : z) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-error "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'inline'" }
; // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-error "'template for' only available with" "" { target c++23_down } .-2 }
}
void
baz ()
{
foo <0> ();
bar <0> ();
}

View File

@ -0,0 +1,110 @@
// C++26 P1306R5 - Expansion statements
// { dg-do compile { target c++11 } }
// { dg-options "" }
struct S { int y; } z[3];
template <int N>
void
foo ()
{
template for (static auto [ a ] : {}) // { dg-warning "for-range-declaration cannot be 'static'" }
; // { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
// { dg-warning "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-3 }
template for (thread_local auto [ a ] : {}) // { dg-warning "for-range-declaration cannot be 'thread_local'" }
; // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 }
// { dg-warning "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-3 }
template for (__thread auto [ a ] : {}) // { dg-warning "for-range-declaration cannot be '__thread'" }
; // { dg-warning "function-scope 'structured binding' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-warning "structured binding declaration can be '__thread' only in" "" { target c++17_down } .-2 }
// { dg-warning "structured bindings only available with" "" { target c++14_down } .-3 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-4 }
template for (register auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'register'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (extern auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'extern'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'mutable'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (virtual auto [ a ] : {}) // { dg-error "'virtual' outside class declaration" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (explicit auto [ a ] : {}) // { dg-error "'explicit' outside class declaration" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (friend auto [ a ] : {}) // { dg-error "'friend' used outside of class" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (typedef auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'typedef'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'consteval'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto [ a ] : {}) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto [ a ] : {}) // { dg-error "structured binding declaration cannot be 'inline'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
}
template <int N>
void
bar ()
{
template for (static auto [ a ] : z) // { dg-warning "for-range-declaration cannot be 'static'" }
; // { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
// { dg-warning "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-3 }
template for (thread_local auto [ a ] : z) // { dg-warning "for-range-declaration cannot be 'thread_local'" }
; // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 }
// { dg-warning "structured bindings only available with" "" { target c++14_down } .-2 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-3 }
template for (__thread auto [ a ] : z) // { dg-warning "for-range-declaration cannot be '__thread'" }
; // { dg-warning "function-scope 'structured binding' implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
// { dg-warning "structured binding declaration can be '__thread' only in" "" { target c++17_down } .-2 }
// { dg-warning "structured bindings only available with" "" { target c++14_down } .-3 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-4 }
template for (register auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'register'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (extern auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'extern'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (mutable auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'mutable'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (virtual auto [ a ] : z) // { dg-error "'virtual' outside class declaration" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (explicit auto [ a ] : z) // { dg-error "'explicit' outside class declaration" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (friend auto [ a ] : z) // { dg-error "'friend' used outside of class" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
template for (typedef auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'typedef'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
#if __cplusplus >= 202002L
template for (consteval auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'consteval'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
template for (constinit auto [ a ] : z) // { dg-error "for-range-declaration cannot be 'constinit'" "" { target c++20 } }
; // { dg-warning "'template for' only available with" "" { target { c++20 && c++23_down } } .-1 }
#endif
template for (inline auto [ a ] : z) // { dg-error "structured binding declaration cannot be 'inline'" }
; // { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
// { dg-warning "'template for' only available with" "" { target c++23_down } .-2 }
}
void
baz ()
{
foo <0> ();
bar <0> ();
}

View File

@ -652,3 +652,9 @@
#elif __cpp_trivial_relocatability != 202502
# error "__cpp_trivial_relocatability != 202502"
#endif
#ifndef __cpp_expansion_statements
# error "__cpp_expansion_statements"
#elif __cpp_expansion_statements != 202506
# error "__cpp_expansion_statements != 202506"
#endif