diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 97c3ecb7d1616c0a3f566d62eb03615e1b9098ea..73a07055a86ea3f28663c306d88fa296b3f7461c 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1034,7 +1034,11 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_fold_expressions=201603L"); if (cxx_dialect <= cxx17) cpp_define (pfile, "__cpp_nontype_template_args=201411L"); - cpp_define (pfile, "__cpp_range_based_for=201603L"); + if (!flag_range_for_ext_temps) + cpp_define (pfile, "__cpp_range_based_for=201603L"); + else + /* This is the C++23 or -std=c++17 -frange-for-ext-temps value. */ + cpp_define (pfile, "__cpp_range_based_for=202211L"); if (cxx_dialect <= cxx17) cpp_define (pfile, "__cpp_constexpr=201603L"); cpp_define (pfile, "__cpp_if_constexpr=201606L"); diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc index b5ce1466e5d1f2efab5dc1da145c6e618605c9e7..620a3c1353a631836093b7b3da55e0f612deb8ad 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -1617,6 +1617,7 @@ c_find_nested_loop_xform_r (tree *tp, int *walk_subtrees, void *) *walk_subtrees = 1; break; case TRY_FINALLY_EXPR: + case CLEANUP_POINT_EXPR: *walk_subtrees = 1; break; default: diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 69dd528102053d9f46db30d49bf3d8386fa5ce1b..86163ddb4eddbb2be43b79338ae4cbd431dea447 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1160,6 +1160,21 @@ c_common_post_options (const char **pfilename) if (cxx_dialect >= cxx20) flag_concepts = 1; + /* Enable lifetime extension of range based for temporaries for C++23. + Diagnose -std=c++23 -fno-range-for-ext-temps. */ + if (cxx_dialect >= cxx23) + { + if (OPTION_SET_P (flag_range_for_ext_temps) + && !flag_range_for_ext_temps) + error ("%<-fno-range-for-ext-temps%> is incompatible with C++23"); + flag_range_for_ext_temps = 1; + } + /* Otherwise default to enabled in GNU modes but allow user to override. */ + else if (cxx_dialect >= cxx11 + && !flag_iso + && !OPTION_SET_P (flag_range_for_ext_temps)) + flag_range_for_ext_temps = 1; + /* -fimmediate-escalation has no effect when immediate functions are not supported. */ if (flag_immediate_escalation && cxx_dialect < cxx20) diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 98a35f043c762e7fe27228ca2589939e1d5de601..b5983093e24ae81fd41f993ffc808915f46f565c 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -2209,6 +2209,10 @@ fprintf-return-value C ObjC C++ ObjC++ LTO Optimization Var(flag_printf_return_value) Init(1) Treat known sprintf return values as constants. +frange-for-ext-temps +C++ ObjC++ Var(flag_range_for_ext_temps) +Enable lifetime extension of range based for temporaries. + freplace-objc-classes ObjC ObjC++ LTO Var(flag_replace_objc_classes) Used in Fix-and-Continue mode to indicate that object files may be swapped in at runtime. diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 70783baac242c1b6b5e9e5e2afaa1ac7d98e01db..309ab01d12d3cd92b2473f42e763a18a0b3b5210 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -14564,6 +14564,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups, if (processing_template_decl) return init; + /* P2718R0 - ignore temporaries in C++23 for-range-initializer, those + have all extended lifetime. */ + if (DECL_NAME (decl) == for_range__identifier + && flag_range_for_ext_temps) + return init; + maybe_warn_dangling_reference (decl, init); if (TYPE_REF_P (type)) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4809576b654dd949c579cce0b63de64d389e9054..7c438eca16d6e577d98b9a241f7669ec497a6511 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7474,7 +7474,8 @@ extern bool maybe_clone_body (tree); extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool, tree, bool); extern void cp_convert_omp_range_for (tree &, tree &, tree &, - tree &, tree &, tree &, tree &, tree &); + tree &, tree &, tree &, tree &, tree &, + bool); extern void cp_finish_omp_range_for (tree, tree); extern bool cp_maybe_parse_omp_decl (tree, tree); extern bool parsing_nsdmi (void); @@ -7809,6 +7810,7 @@ extern tree begin_for_stmt (tree, tree); extern void finish_init_stmt (tree); extern void finish_for_cond (tree, tree, bool, tree, bool); extern void finish_for_expr (tree, tree); +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); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 2190ede745b868ba32e3438dc1552b24989eb264..6a7ba416cf8a0ca541a6449738313e921a025868 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8157,6 +8157,11 @@ initialize_local_var (tree decl, tree init, bool decomp) code emitted by cp_finish_decomp. */ if (decomp) current_stmt_tree ()->stmts_are_full_exprs_p = 0; + /* P2718R0 - avoid CLEANUP_POINT_EXPR for range-for-initializer, + temporaries from there should have lifetime extended. */ + else if (DECL_NAME (decl) == for_range__identifier + && flag_range_for_ext_temps) + current_stmt_tree ()->stmts_are_full_exprs_p = 0; else current_stmt_tree ()->stmts_are_full_exprs_p = 1; finish_expr_stmt (init); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 35c266659e46370a059a57a6ed97220d1c43bf91..83ae38a33ab256603dd6c3ce3649ab5bfd43883a 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -14480,6 +14480,15 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr, { range_temp = build_range_temp (range_expr); pushdecl (range_temp); + if (flag_range_for_ext_temps) + { + /* P2718R0 - put the range_temp declaration and everything + until end of the range for body into an extra STATEMENT_LIST + which will have CLEANUP_POINT_EXPR around it, so that all + temporaries are destroyed at the end of it. */ + gcc_assert (FOR_INIT_STMT (statement) == NULL_TREE); + FOR_INIT_STMT (statement) = push_stmt_list (); + } cp_finish_decl (range_temp, range_expr, /*is_constant_init*/false, NULL_TREE, LOOKUP_ONLYCONVERTING); @@ -44629,7 +44638,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser, void cp_convert_omp_range_for (tree &this_pre_body, tree &sl, tree &decl, tree &orig_decl, tree &init, - tree &orig_init, tree &cond, tree &incr) + tree &orig_init, tree &cond, tree &incr, + bool tmpl_p) { tree begin, end, range_temp_decl = NULL_TREE; tree iter_type, begin_expr, end_expr; @@ -44687,11 +44697,29 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl, else { range_temp = build_range_temp (init); - DECL_NAME (range_temp) = NULL_TREE; + tree name = DECL_NAME (range_temp); + /* Temporarily clear DECL_NAME of the __for_range temporary. + While it contains a space at the end and outside of templates + it works even without doing this, when cp_convert_omp_range_for + is called from tsubst_omp_for_iterator, all the associated loops + are in a single scope and so loop nests with 2 or more range + based for loops would error. */ + if (tmpl_p) + DECL_NAME (range_temp) = NULL_TREE; pushdecl (range_temp); + /* Restore the name back. This is needed for cp_finish_decl + lifetime extension of temporaries, and can be helpful for user + during debugging after the DECL_NAME is changed to __for_range + without space at the end. */ + if (tmpl_p) + DECL_NAME (range_temp) = name; cp_finish_decl (range_temp, init, /*is_constant_init*/false, NULL_TREE, LOOKUP_ONLYCONVERTING); + /* And clear the name again. pop_scope requires that the name + used during pushdecl didn't change. */ + if (tmpl_p) + DECL_NAME (range_temp) = NULL_TREE; range_temp_decl = range_temp; range_temp = convert_from_reference (range_temp); } @@ -44791,7 +44819,7 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl, the whole loop nest. The remaining elements are decls of derived decomposition variables that are bound inside the loop body. This structure is further mangled by finish_omp_for into the form required - for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node. */\ + for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node. */ unsigned decomp_cnt = decomp ? decomp->count : 0; tree v = make_tree_vec (decomp_cnt + 3); TREE_VEC_ELT (v, 0) = range_temp_decl; @@ -45360,7 +45388,7 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p) cp_convert_omp_range_for (this_pre_body, sl, decl, orig_decl, init, orig_init, - cond, incr); + cond, incr, false); if (omp_for_parse_state->ordered_cl) error_at (OMP_CLAUSE_LOCATION (omp_for_parse_state->ordered_cl), @@ -45623,11 +45651,30 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p) /* Pop and remember the init block. */ if (sl) - add_stmt (pop_stmt_list (sl)); + { + sl = pop_stmt_list (sl); + /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in + for-range-initializer whose lifetime is extended are destructed + here. */ + if (flag_range_for_ext_temps + && is_range_for + && !processing_template_decl) + sl = maybe_cleanup_point_expr_void (sl); + add_stmt (sl); + } + tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE }; + if (is_range_for && !processing_template_decl) + find_range_for_decls (range_for_decl); finish_compound_stmt (init_scope); init_block = pop_stmt_list (init_block); omp_for_parse_state->init_blockv[depth] = init_block; + if (is_range_for && !processing_template_decl) + for (int i = 0; i < 3; i++) + if (range_for_decl[i]) + DECL_NAME (range_for_decl[i]) + = cp_global_trees[CPTI_FOR_RANGE_IDENTIFIER + i]; + /* Return the init placeholder rather than the remembered init block. Again, this is just a unique cookie that will be used to reassemble code pieces when the entire omp for statement has been parsed. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index e826206be1644991b790a8dada244022880a818a..a3a697dd126351a47d8400b4e1ef28d67da9d8a7 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -18127,7 +18127,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv, tree orig_decl = NULL_TREE; tree init_sl = NULL_TREE; cp_convert_omp_range_for (this_pre_body, init_sl, decl, orig_decl, init, - orig_init, cond, incr); + orig_init, cond, incr, true); if (orig_decl) { if (orig_declv == NULL_TREE) @@ -19156,6 +19156,18 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) RECUR (OMP_FOR_PRE_BODY (t)); pre_body = pop_stmt_list (pre_body); + tree sl = NULL_TREE; + if (flag_range_for_ext_temps + && OMP_FOR_INIT (t) != NULL_TREE + && !processing_template_decl) + for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++) + if (TREE_VEC_ELT (OMP_FOR_INIT (t), i) + && TREE_VEC_ELT (OMP_FOR_COND (t), i) == global_namespace) + { + sl = push_stmt_list (); + break; + } + if (OMP_FOR_INIT (t) != NULL_TREE) for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++) { @@ -19211,9 +19223,34 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) add_stmt (t); } + if (sl) + { + /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in + for-range-initializer whose lifetime is extended are destructed + here. */ + sl = pop_stmt_list (sl); + sl = maybe_cleanup_point_expr_void (sl); + add_stmt (sl); + } + add_stmt (finish_omp_for_block (finish_omp_structured_block (stmt), t)); pop_omp_privatization_clauses (r); + + if (any_range_for && !processing_template_decl && t) + for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++) + if (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i) + && TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), + i)) == TREE_LIST) + { + tree v = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i); + if (TREE_CHAIN (v) == NULL_TREE + || TREE_CODE (TREE_CHAIN (v)) != TREE_VEC) + continue; + v = TREE_VEC_ELT (TREE_CHAIN (v), 0); + gcc_assert (VAR_P (v) && DECL_NAME (v) == NULL_TREE); + DECL_NAME (v) = for_range_identifier; + } } break; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 8219d6410b8e6e644411d951a95e06668710cb02..a061704e1a27e329488fa4ac871f9790bf7d64bc 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -1637,6 +1637,33 @@ finish_for_expr (tree expr, tree for_stmt) FOR_EXPR (for_stmt) = expr; } +/* During parsing of the body, range for uses "__for_{range,begin,end} " + decl names to make those unaccessible by code in the body. + Find those decls and store into RANGE_FOR_DECL array, so that they + can be changed later to ones with underscore instead of space, so that + it can be inspected in the debugger. */ + +void +find_range_for_decls (tree range_for_decl[3]) +{ + gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1 + && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2 + && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3 + && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3 + && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3); + for (int i = 0; i < 3; i++) + { + tree id = cp_global_trees[CPTI_FOR_RANGE__IDENTIFIER + i]; + if (IDENTIFIER_BINDING (id) + && IDENTIFIER_BINDING (id)->scope == current_binding_level) + { + range_for_decl[i] = IDENTIFIER_BINDING (id)->value; + gcc_assert (VAR_P (range_for_decl[i]) + && DECL_ARTIFICIAL (range_for_decl[i])); + } + } +} + /* Finish the body of a for-statement, which may be given by FOR_STMT. The increment-EXPR for the loop must be provided. @@ -1670,21 +1697,20 @@ finish_for_stmt (tree for_stmt) Change it to ones with underscore instead of space, so that it can be inspected in the debugger. */ tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE }; - gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1 - && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2 - && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3 - && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3 - && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3); - for (int i = 0; i < 3; i++) - { - tree id = cp_global_trees[CPTI_FOR_RANGE__IDENTIFIER + i]; - if (IDENTIFIER_BINDING (id) - && IDENTIFIER_BINDING (id)->scope == current_binding_level) - { - range_for_decl[i] = IDENTIFIER_BINDING (id)->value; - gcc_assert (VAR_P (range_for_decl[i]) - && DECL_ARTIFICIAL (range_for_decl[i])); - } + find_range_for_decls (range_for_decl); + + /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in + for-range-initializer whose lifetime is extended are destructed + here. */ + if (flag_range_for_ext_temps + && range_for_decl[0] + && FOR_INIT_STMT (for_stmt)) + { + tree stmt = pop_stmt_list (FOR_INIT_STMT (for_stmt)); + FOR_INIT_STMT (for_stmt) = NULL_TREE; + stmt = build_stmt (EXPR_LOCATION (for_stmt), EXPR_STMT, stmt); + stmt = maybe_cleanup_point_expr_void (stmt); + add_stmt (stmt); } add_stmt (do_poplevel (scope)); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 1f9f3386bf9766796b8129298dbe0951b9a6c9e3..bdbbea53666e1b41dd78843937772d8bbf17b022 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -214,7 +214,7 @@ in the following sections. @xref{C++ Dialect Options,,Options Controlling C++ Dialect}. @gccoptlist{-fabi-version=@var{n} -fno-access-control -faligned-new=@var{n} -fargs-in-order=@var{n} -fchar8_t -fcheck-new --fconstexpr-depth=@var{n} -fconstexpr-cache-depth=@var{n} +-fconcepts -fconstexpr-depth=@var{n} -fconstexpr-cache-depth=@var{n} -fconstexpr-loop-limit=@var{n} -fconstexpr-ops-limit=@var{n} -fno-elide-constructors -fno-enforce-eh-specs @@ -233,7 +233,7 @@ in the following sections. -fnew-ttp-matching -fno-nonansi-builtins -fnothrow-opt -fno-operator-names -fno-optional-diags --fno-pretty-templates +-fno-pretty-templates -frange-for-ext-temps -fno-rtti -fsized-deallocation -ftemplate-backtrace-limit=@var{n} -ftemplate-depth=@var{n} @@ -3614,6 +3614,15 @@ the default template arguments for that template. If either of these behaviors make it harder to understand the error message rather than easier, you can use @option{-fno-pretty-templates} to disable them. +@opindex frange-for-ext-temps +@item -frange-for-ext-temps +Enable lifetime extension of C++ range based for temporaries. +With @option{-std=c++23} and above this is part of the language standard, +so lifetime of the temporaries is extended until the end of the loop +regardless of this option. This option allows enabling that behavior also +in earlier versions of the standard and is enabled by default in the +GNU dialects, from @option{-std=gnu++11} until @option{-std=gnu++20}. + @opindex fno-rtti @opindex frtti @item -fno-rtti diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 9713e684e830a271ad1fc3fac7cf77bba36fdc7d..f4c5f5770474536adc14af866e04e3cef71eeede 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -972,6 +972,7 @@ find_combined_omp_for (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 1; break; case TRY_FINALLY_EXPR: + case CLEANUP_POINT_EXPR: pdata[0] = tp; *walk_subtrees = 1; break; @@ -4226,6 +4227,7 @@ find_nested_loop_xform (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 1; break; case TRY_FINALLY_EXPR: + case CLEANUP_POINT_EXPR: pdata[0] = tp; *walk_subtrees = 1; break; diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index a1ddc81cefdebe69b3eb8ef38ea165376bdf7948..4033552b2ebc0bec8e3b64b557dee731f58c9cc6 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -43,8 +43,8 @@ #ifndef __cpp_range_based_for # error "__cpp_range_based_for" -#elif __cpp_range_based_for != 201603 -# error "__cpp_range_based_for != 201603" +#elif __cpp_range_based_for != 202211 +# error "__cpp_range_based_for != 202211" #endif #ifndef __cpp_decltype diff --git a/gcc/testsuite/g++.dg/cpp23/range-for1.C b/gcc/testsuite/g++.dg/cpp23/range-for1.C new file mode 100644 index 0000000000000000000000000000000000000000..2aa8a5e93328344de67b86dc75945e247a0894a9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for1.C @@ -0,0 +1,222 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } + +#ifndef RANGE_FOR_EXT_TEMPS +#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L) +#else +#if __cplusplus >= 201703L +#if RANGE_FOR_EXT_TEMPS +static_assert (__cpp_range_based_for >= 202211L, ""); +#else +static_assert (__cpp_range_based_for >= 201603L + && __cpp_range_based_for < 202211L, ""); +#endif +#else +static_assert (__cpp_range_based_for >= 200907L + && __cpp_range_based_for < 201603L, ""); +#endif +#endif + +extern "C" void abort (); +void check (bool); + +struct S +{ + S () { ++s; } + S (const S &) { ++s; } + ~S () { check (true); --s; } + static int s; +}; + +int S::s = -1; +S sv; + +struct T +{ + T (const S &, const S &) { ++t; } + T (const T &) { ++t; } + ~T () { check (false); --t; } + static int t; +}; + +int T::t = -1; +T tv (sv, sv); +int a[4]; +int c; + +void +check (bool is_s) +{ + if (c) + { + if (is_s) + { + if (T::t != (c == 1)) + abort (); + } + else + { + if (S::s != (c == 1 ? 0 : 2)) + abort (); + } + } +} + +template <typename T> +int * +begin (const T &) +{ + return &a[0]; +} + +template <typename T> +int * +end (const T &) +{ + return &a[4]; +} + +const S & +foo (const S &) +{ + return sv; +} + +const T & +foo (const T &) +{ + return tv; +} + +void +bar () +{ + if (S::s != 0) + abort (); + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + c = 1 + RANGE_FOR_EXT_TEMPS; + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 2; + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 0; +} + +template <int N> +void +baz () +{ + if (S::s != 0) + abort (); + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + c = 1 + RANGE_FOR_EXT_TEMPS; + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 2; + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 0; +} + +template <typename S, typename T> +void +qux () +{ + if (S::s != 0) + abort (); + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + c = 1 + RANGE_FOR_EXT_TEMPS; + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 2; + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 0; +} + +int +main () +{ + bar (); + baz <0> (); + qux <S, T> (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/range-for2.C b/gcc/testsuite/g++.dg/cpp23/range-for2.C new file mode 100644 index 0000000000000000000000000000000000000000..e40e6d32a27ba5243399cd7ed81f4187d0e3de77 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for2.C @@ -0,0 +1,231 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } + +#ifndef RANGE_FOR_EXT_TEMPS +#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L) +#endif + +extern "C" void abort (); + +int a[4]; + +template <typename T> +int * +begin (const T &) +{ + return &a[0]; +} + +template <typename T> +int * +end (const T &) +{ + return &a[4]; +} + +struct S +{ + S () { ++s; } + S (const S &) { ++s; } + ~S () { --s; } + static int s; +}; + +int S::s; + +template <typename T> +struct U +{ + T t; + U () { ++u; } + U (const U &) { ++u; } + ~U () { --u; } + const int *begin () const { return ::begin (t); } + const int *end () const { return ::end (t); } + U &foo () { return *this; } + U bar () { return U (); } + static int u; +}; + +template <typename T> +int U<T>::u; + +template <typename T> +U<T> +foo () +{ + return U<T> {}; +} + +template <typename T> +T +fred (const T &, const T & = T{}, const T & = T{}) +{ + return T {}; +} + +void +bar () +{ + int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + if (S::s != 0) + abort (); + for (auto x : S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : (void) S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : static_cast<void> (S ()), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo <S> ().foo ().bar ().foo ().bar ().foo ().bar ()) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + if (U<S>::u != S::s) + abort (); + } + if (S::s != 0 || U<S>::u != 0) + abort (); + for (auto x : fred (S {})) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : fred (fred (S {}, S {}))) + { + if (S::s != 1 + 6 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); +} + +template <int N> +void +baz () +{ + int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + if (S::s != 0) + abort (); + for (auto x : S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : (void) S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : static_cast<void> (S ()), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo <S> ().foo ().bar ().foo ().bar ().foo ().bar ()) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + if (U<S>::u != S::s) + abort (); + } + if (S::s != 0 || U<S>::u != 0) + abort (); + for (auto x : fred (S {})) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : fred (fred (S {}, S {}))) + { + if (S::s != 1 + 6 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); +} + +template <typename S> +void +qux () +{ + int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + if (S::s != 0) + abort (); + for (auto x : S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : (void) S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : static_cast<void> (S ()), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo <S> ().foo ().bar ().foo ().bar ().foo ().bar ()) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + if (U<S>::u != S::s) + abort (); + } + if (S::s != 0 || U<S>::u != 0) + abort (); + for (auto x : fred (S {})) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : fred (fred (S {}, S {}))) + { + if (S::s != 1 + 6 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); +} + +int +main () +{ + bar (); + baz <0> (); + qux <S> (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/range-for3.C b/gcc/testsuite/g++.dg/cpp23/range-for3.C new file mode 100644 index 0000000000000000000000000000000000000000..301e25886ec613c611c45a741fb3e2f59c1f1534 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for3.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } +// Verify -frange-for-ext-temps is set by default in -std=gnu++* modes. +// { dg-options "" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for1.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for4.C b/gcc/testsuite/g++.dg/cpp23/range-for4.C new file mode 100644 index 0000000000000000000000000000000000000000..f8c380d32c721236cbafdff4ab9be7d8d5c2b529 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for4.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } +// Verify -frange-for-ext-temps is set by default in -std=gnu++* modes. +// { dg-options "" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for2.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for5.C b/gcc/testsuite/g++.dg/cpp23/range-for5.C new file mode 100644 index 0000000000000000000000000000000000000000..c2cd1248bf6899895981537514632d6f4e936bd7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for5.C @@ -0,0 +1,8 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target { c++11 && c++20_down } } } +// { dg-options "-fno-range-for-ext-temps" } + +#if __cplusplus <= 202002L +#define RANGE_FOR_EXT_TEMPS 0 +#endif +#include "range-for1.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for6.C b/gcc/testsuite/g++.dg/cpp23/range-for6.C new file mode 100644 index 0000000000000000000000000000000000000000..8bf8656e5ef7db2d80f6dfa127211100aa20027e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for6.C @@ -0,0 +1,8 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target { c++11 && c++20_down } } } +// { dg-options "-fno-range-for-ext-temps" } + +#if __cplusplus <= 202002L +#define RANGE_FOR_EXT_TEMPS 0 +#endif +#include "range-for2.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for7.C b/gcc/testsuite/g++.dg/cpp23/range-for7.C new file mode 100644 index 0000000000000000000000000000000000000000..99d9bd85bc36f071ea944cb60237ee31ca5c0cde --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for7.C @@ -0,0 +1,6 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++17_only } } +// { dg-options "-std=c++17 -frange-for-ext-temps" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for1.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for8.C b/gcc/testsuite/g++.dg/cpp23/range-for8.C new file mode 100644 index 0000000000000000000000000000000000000000..3b2efbc7e41aa5596c5f3279c2312c55149a565d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for8.C @@ -0,0 +1,6 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11_only } } +// { dg-options "-std=c++11 -frange-for-ext-temps" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for2.C" diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 52c89e498fb0763e8a6e6c65baf114357e6f0519..c387a7dfe6095d5799563927fa92b0a0b4d28ce7 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -43,8 +43,8 @@ #ifndef __cpp_range_based_for # error "__cpp_range_based_for" -#elif __cpp_range_based_for != 201603 -# error "__cpp_range_based_for != 201603" +#elif __cpp_range_based_for != 202211 +# error "__cpp_range_based_for != 202211" #endif #ifndef __cpp_decltype diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C index 0343bcf226b62c9b911787e4f461d08e3d20d61f..ada70dd683a327c5ebbc0547a1d1b3f5205076c0 100644 --- a/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C @@ -1,5 +1,5 @@ // { dg-do compile { target c++17 } } -// { dg-options "-Wdangling-reference" } +// { dg-additional-options "-Wdangling-reference" } // { dg-skip-if "requires hosted libstdc++ for string" { ! hostedlib } } // Check that we warn here even without -Wsystem-headers. @@ -11,5 +11,5 @@ auto f() -> std::optional<std::string>; void g () { - for (char c : f().value()) { (void) c; } // { dg-warning "dangling reference" } + for (char c : f().value()) { (void) c; } // { dg-warning "dangling reference" "" { target c++20_down } } } diff --git a/libgomp/testsuite/libgomp.c++/range-for-1.C b/libgomp/testsuite/libgomp.c++/range-for-1.C new file mode 100644 index 0000000000000000000000000000000000000000..c92f9c4e145329ecc79ce835d919566981a36c4a --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-1.C @@ -0,0 +1,250 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=c++17" } +// { dg-require-effective-target tls_runtime } + +#ifndef RANGE_FOR_EXT_TEMPS +#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L) +#endif + +extern "C" void abort (); +void check (bool); + +struct S +{ + S () { ++s; } + S (const S &) { ++s; } + ~S () { check (true); --s; } + [[omp::decl (threadprivate)]] static int s; +}; +int S::s; +S sv; +struct T +{ + T (const S &, const S &) { ++t; } + T (const T &) { ++t; } + ~T () { check (false); --t; } + [[omp::decl (threadprivate)]] static int t; +}; +int T::t; +T tv (sv, sv); +int a[4]; +int c; + +void +check (bool is_s) +{ + if (c) + { + if (is_s) + { + if (T::t != (c == 1)) + abort (); + } + else + { + if (S::s != (c == 1 ? 0 : 2)) + abort (); + } + } +} + +template <typename T> +int * +begin (const T &) +{ + return &a[0]; +} + +template <typename T> +int * +end (const T &) +{ + return &a[4]; +} + +const S & +foo (const S &) +{ + return sv; +} + +const T & +foo (const T &) +{ + return tv; +} + +void +bar () +{ + #pragma omp parallel num_threads (4) + { + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + } + c = 1 + RANGE_FOR_EXT_TEMPS; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 2; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 0; +} + +template <int N> +void +baz () +{ + #pragma omp parallel num_threads (4) + { + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + } + c = 1 + RANGE_FOR_EXT_TEMPS; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 2; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 0; +} + +template <typename S, typename T> +void +qux () +{ + #pragma omp parallel num_threads (4) + { + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + } + c = 1 + RANGE_FOR_EXT_TEMPS; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 2; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 0; +} + +int +main () +{ + S::s--; + T::t--; + bar (); + baz <0> (); + qux <S, T> (); +} diff --git a/libgomp/testsuite/libgomp.c++/range-for-2.C b/libgomp/testsuite/libgomp.c++/range-for-2.C new file mode 100644 index 0000000000000000000000000000000000000000..8c25140cdd5fdef97f846fe630a1fee04cfcbdc2 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-2.C @@ -0,0 +1,6 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=c++23" } +// { dg-require-effective-target tls_runtime } + +#include "range-for-1.C" diff --git a/libgomp/testsuite/libgomp.c++/range-for-3.C b/libgomp/testsuite/libgomp.c++/range-for-3.C new file mode 100644 index 0000000000000000000000000000000000000000..919fe85b374d9c284784ddeb4430314511cb5937 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-3.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=c++17 -frange-for-ext-temps" } +// { dg-require-effective-target tls_runtime } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for-1.C" diff --git a/libgomp/testsuite/libgomp.c++/range-for-4.C b/libgomp/testsuite/libgomp.c++/range-for-4.C new file mode 100644 index 0000000000000000000000000000000000000000..3c10e7349af7140d5e64fa3d515856789a36023f --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-4.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=gnu++17" } +// { dg-require-effective-target tls_runtime } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for-1.C" diff --git a/libgomp/testsuite/libgomp.c++/range-for-5.C b/libgomp/testsuite/libgomp.c++/range-for-5.C new file mode 100644 index 0000000000000000000000000000000000000000..d8c84c3266952014b9dfadbaed7fd639c7e36b40 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-5.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=gnu++17 -fno-range-for-ext-temps" } +// { dg-require-effective-target tls_runtime } + +#define RANGE_FOR_EXT_TEMPS 0 +#include "range-for-1.C"