diff options
author | Jakub Jelinek <jakub@redhat.com> | 2025-08-13 22:07:27 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2025-08-13 22:10:18 +0200 |
commit | 458773ac7bca792db044ad6c241f566c9ba87cb7 (patch) | |
tree | 306c5f950a9f2c08c7d04ef0f049ab1688c6f00f /gcc | |
parent | 5fedaa2347939dcad24581608520067ec6560758 (diff) | |
download | gcc-458773ac7bca792db044ad6c241f566c9ba87cb7.zip gcc-458773ac7bca792db044ad6c241f566c9ba87cb7.tar.gz gcc-458773ac7bca792db044ad6c241f566c9ba87cb7.tar.bz2 |
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.
Diffstat (limited to 'gcc')
37 files changed, 2952 insertions, 164 deletions
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 5476d10..0ba6151 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -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"); diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 63cad2a..02cef63 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -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); diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index eabf7f8..223240a 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -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); diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc index 7665b94..ee1c0ba 100644 --- a/gcc/cp/cp-objcp-common.cc +++ b/gcc/cp/cp-objcp-common.cc @@ -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); diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index bb5aaf9..b1e3697 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -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) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index fb8e0d8..f45b9bb 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index 5f24015..4916bf6 100644 --- a/gcc/cp/cxx-pretty-print.cc +++ b/gcc/cp/cxx-pretty-print.cc @@ -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: diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 5aa8203..1d12bef 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -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) diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 2bbc618..c6c9dfc 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -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; } diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index cd35c7a..8ef9f9e 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -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") diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index f5b36c9..a805f42 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -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: diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 2fa736b..6a3c0df 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -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. */ diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 1dbe35b..7d6ad02 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -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; diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index f9ed801..3a17be9 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -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. diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index b6b13ed..c7ea337 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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 diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 0c7788d..7f6f908 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp1z/decomp64.C b/gcc/testsuite/g++.dg/cpp1z/decomp64.C new file mode 100644 index 0000000..e715b9e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/decomp64.C @@ -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 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C new file mode 100644 index 0000000..077b70c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt10.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt10.C new file mode 100644 index 0000000..dfa286b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt10.C @@ -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 } diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C new file mode 100644 index 0000000..0156806 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C @@ -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> (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt12.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt12.C new file mode 100644 index 0000000..3b31e37 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt12.C @@ -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), ""); diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C new file mode 100644 index 0000000..087c707 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt14.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt14.C new file mode 100644 index 0000000..ffdbe07 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt14.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C new file mode 100644 index 0000000..8f8ed75 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C @@ -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 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C new file mode 100644 index 0000000..e6f914e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C @@ -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 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt17.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt17.C new file mode 100644 index 0000000..1d4e310 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt17.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C new file mode 100644 index 0000000..a3e7dd76 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C @@ -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 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C new file mode 100644 index 0000000..59983d1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C new file mode 100644 index 0000000..590638e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C new file mode 100644 index 0000000..b4b16bb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt4.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt4.C new file mode 100644 index 0000000..8fa9252 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt4.C @@ -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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt5.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt5.C new file mode 100644 index 0000000..0288cf5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt5.C @@ -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); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt6.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt6.C new file mode 100644 index 0000000..f6c4771 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt6.C @@ -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 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt7.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt7.C new file mode 100644 index 0000000..583521c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt7.C @@ -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 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt8.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt8.C new file mode 100644 index 0000000..3d9e91d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt8.C @@ -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> (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt9.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt9.C new file mode 100644 index 0000000..c2d0cd8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt9.C @@ -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> (); +} diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 9284bc2..4fe0680 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -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 |