From 458773ac7bca792db044ad6c241f566c9ba87cb7 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Wed, 13 Aug 2025 22:07:27 +0200 Subject: 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 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. --- gcc/c-family/c-cppbuiltin.cc | 1 + gcc/cp/call.cc | 5 +- gcc/cp/constexpr.cc | 1 + gcc/cp/cp-objcp-common.cc | 1 + gcc/cp/cp-tree.def | 5 + gcc/cp/cp-tree.h | 33 +- gcc/cp/cxx-pretty-print.cc | 23 ++ gcc/cp/decl.cc | 167 +++++++- gcc/cp/decl2.cc | 8 + gcc/cp/error.cc | 19 +- gcc/cp/name-lookup.cc | 6 +- gcc/cp/name-lookup.h | 5 +- gcc/cp/parser.cc | 543 +++++++++++++++++++------- gcc/cp/parser.h | 6 +- gcc/cp/pt.cc | 466 +++++++++++++++++++++- gcc/cp/semantics.cc | 17 +- gcc/testsuite/g++.dg/cpp1z/decomp64.C | 16 + gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C | 216 ++++++++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt10.C | 17 + gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C | 93 +++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt12.C | 54 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C | 97 +++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt14.C | 75 ++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C | 47 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C | 68 ++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt17.C | 37 ++ gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C | 58 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C | 94 +++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C | 208 ++++++++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C | 197 ++++++++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt4.C | 35 ++ gcc/testsuite/g++.dg/cpp26/expansion-stmt5.C | 96 +++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt6.C | 88 +++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt7.C | 89 +++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt8.C | 109 ++++++ gcc/testsuite/g++.dg/cpp26/expansion-stmt9.C | 110 ++++++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 6 + 37 files changed, 2952 insertions(+), 164 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/decomp64.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt10.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt11.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt12.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt13.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt14.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt16.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt17.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt19.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt4.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt5.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt6.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt7.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt8.C create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt9.C (limited to 'gcc') 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 **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 %