On 4/7/26 2:55 AM, Jakub Jelinek wrote:
> Hi!
>
> The following patch implements CWG3140, which allows non-constexpr
> iterating expansion statements over non-constexpr ranges like std::array
> where the range size is still known at compile time.
> On the other side it breaks iterating expansion statements without constexpr
> keyword on range-decl over ranges where to find out the range size the range
> needs to be accessed. In some cases one will have to just add the constexpr
> keyword, in other cases even that doesn't have to work (e.g. when the
> iterator is constexpr except for operator* or some non-constexpr conversion
> operator is involved).
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2026-04-07 Jakub Jelinek <jakub@redhat.com>
>
> * cp-tree.h: Implement C++26 CWG3140 - Allowing expansion over
> non-constant std::array.
> (cp_build_range_for_decls): Change last argument from bool to tree.
> (build_range_temp): Likewise and default it to NULL_TREE rather than
> false.
> * parser.cc (build_range_temp): Remove expansion_stmt_p
> argument, add expansion_stmt_decl. Don't call cp_build_qualified_type
> if expansion_stmt_decl was not declared constexpr.
> (cp_build_range_for_decls): Remove expansion_stmt_p argument, add
> expansion_stmt_decl. Pass expansion_stmt_decl to build_range_temp.
> Don't set TREE_STATIC, TREE_PUBLIC, DECL_COMMON, DECL_INTERFACE_KNOWN,
> DECL_DECLARED_CONSTEXPR_P and TREE_READONLY on range_temp if
> expansion_stmt_decl was not declared constexpr. Don't call
> cp_build_qualified_type on begin type nor set TREE_STATIC etc. on begin
> if expansion_stmt_decl was not declared constexpr. If
> expansion_stmt_decl is non-NULL, don't build end at all, instead pass
> begin_expr and end_expr in end_p[0] and end_p[1] to the caller.
> (cp_convert_range_for): Adjust cp_build_range_for_decls caller.
> * pt.cc (finish_expansion_stmt): Likewise. Use begin_expr and end_expr
> instead of begin and end variables from cp_build_range_for_decls,
> make them non-constant and avoid spurious -Wunused-result/nodiscard
> diagnostics.
>
> * g++.dg/cpp26/expansion-stmt11.C: Expect some extra diagnostics for
> C++11.
> * g++.dg/cpp26/expansion-stmt13.C: Likewise. Make it dg-do compile
> test for c++11_only and dg-do run only for c++14 and above.
> * g++.dg/cpp26/expansion-stmt16.C: Adjust expected diagnostics.
> * g++.dg/cpp26/expansion-stmt19.C: Expect some extra diagnostics for
> C++11. Make it dg-do compile test for c++11_only and dg-do run only
> for c++14 and above.
> * g++.dg/cpp26/expansion-stmt25.C (foo): Test both constexpr
> range-for-decl and non-constexpr, adjust expected diagnostics.
> * g++.dg/cpp26/expansion-stmt30.C: Adjust expected diagnostics.
> * g++.dg/cpp26/expansion-stmt35.C: New test.
> * g++.dg/cpp26/expansion-stmt36.C: New test.
> * g++.dg/cpp26/expansion-stmt37.C: New test.
> * g++.dg/cpp26/expansion-stmt38.C: New test.
>
> --- gcc/cp/cp-tree.h.jj 2026-04-03 20:46:22.845328260 +0200
> +++ gcc/cp/cp-tree.h 2026-04-06 09:59:23.095379300 +0200
> @@ -8028,10 +8028,10 @@ 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_build_range_for_decls (location_t, tree, tree *, tree);
> extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
> tree, bool);
> -extern tree build_range_temp (tree, bool = false);
> +extern tree build_range_temp (tree, tree = NULL_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 &,
> --- gcc/cp/parser.cc.jj 2026-04-04 11:32:27.440454554 +0200
> +++ gcc/cp/parser.cc 2026-04-06 09:59:23.100379216 +0200
> @@ -15854,16 +15854,17 @@ cp_parser_range_for (cp_parser *parser,
> builds up the range temporary. */
>
> tree
> -build_range_temp (tree range_expr, bool expansion_stmt_p /* = false */)
> +build_range_temp (tree range_expr, tree expansion_stmt_decl /* = NULL_TREE */)
> {
> tree range_type, auto_node;
>
> - if (expansion_stmt_p)
> + if (expansion_stmt_decl)
> {
> /* Build const decltype(auto) __range = range_expr;
> - range_expr provided by the caller already is (range_expr). */
> - auto_node = make_decltype_auto ();
> - range_type = cp_build_qualified_type (auto_node, TYPE_QUAL_CONST);
> + range_type = auto_node = make_decltype_auto ();
> + if (DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl))
> + range_type = cp_build_qualified_type (auto_node, TYPE_QUAL_CONST);
> }
> else
> {
> @@ -16007,11 +16008,13 @@ warn_for_range_copy (tree decl, tree exp
>
> /* 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. */
> + __begin VAR_DECL, set *END_P to the __end VAR_DECL. If
> + EXPANSION_STMT_DECL, don't create __end and instead store
> + begin_expr to END_P[0] and end_expr to END_P[1]. */
>
> tree
> cp_build_range_for_decls (location_t loc, tree range_expr, tree *end_p,
> - bool expansion_stmt_p)
> + tree expansion_stmt_decl)
> {
> tree iter_type, begin_expr, end_expr;
>
> @@ -16023,30 +16026,34 @@ cp_build_range_for_decls (location_t loc
> {
> tree range_temp;
>
> - if (!expansion_stmt_p
> + if (!expansion_stmt_decl
> && 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
> {
> - if (expansion_stmt_p)
> + if (expansion_stmt_decl)
> {
> /* Build constexpr decltype(auto) __for_range = (range_expr); */
> location_t range_loc = cp_expr_loc_or_loc (range_expr, loc);
> range_expr
> = finish_parenthesized_expr (cp_expr (range_expr, range_loc));
> - range_temp = build_range_temp (range_expr, true);
> + range_temp = build_range_temp (range_expr, expansion_stmt_decl);
>
> /* When P2686R4 is fully implemented, these 3 sets of TREE_STATIC
> (on range_temp, begin and end) should be removed as per
> - CWG3044. */
> - 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;
> + CWG3044. If expansion_stmt_decl is not constexpr, we don't
> + need the static though. */
> + if (DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl))
> + {
> + 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;
> + }
> }
> else
> /* Build auto &&__for_range = range_expr; */
> @@ -16063,12 +16070,14 @@ cp_build_range_for_decls (location_t loc
> }
>
> /* The new for initialization statement. */
> - if (expansion_stmt_p && !TYPE_REF_P (iter_type))
> + if (expansion_stmt_decl
> + && DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl)
> + && !TYPE_REF_P (iter_type))
> iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST);
> tree begin = build_decl (loc, VAR_DECL, for_begin__identifier, iter_type);
> TREE_USED (begin) = 1;
> DECL_ARTIFICIAL (begin) = 1;
> - if (expansion_stmt_p)
> + if (expansion_stmt_decl && DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl))
> {
> TREE_STATIC (begin) = 1;
> DECL_DECLARED_CONSTEXPR_P (begin) = 1;
> @@ -16079,21 +16088,18 @@ cp_build_range_for_decls (location_t loc
> /*is_constant_init*/false, NULL_TREE,
> LOOKUP_ONLYCONVERTING);
>
> - if (cxx_dialect >= cxx17)
> + if (expansion_stmt_decl)
> {
> - iter_type = cv_unqualified (TREE_TYPE (end_expr));
> - if (expansion_stmt_p && !TYPE_REF_P (iter_type))
> - iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST);
> + end_p[0] = begin_expr;
> + end_p[1] = end_expr;
> + return begin;
> }
> +
> + 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,
> @@ -16149,7 +16155,7 @@ cp_convert_range_for (tree statement, tr
> if (range_decl == error_mark_node)
> range_expr = error_mark_node;
> tree begin
> - = cp_build_range_for_decls (input_location, range_expr, &end, false);
> + = cp_build_range_for_decls (input_location, range_expr, &end, NULL_TREE);
>
> finish_init_stmt (statement);
>
> --- gcc/cp/pt.cc.jj 2026-04-04 11:07:32.000119351 +0200
> +++ gcc/cp/pt.cc 2026-04-06 10:59:16.537372085 +0200
> @@ -33329,34 +33329,46 @@ finish_expansion_stmt (tree expansion_st
> 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 exprs[2];
> + begin = cp_build_range_for_decls (loc, expansion_init, exprs, range_decl);
> + if (!error_operand_p (begin)
> + && !error_operand_p (exprs[0])
> + && !error_operand_p (exprs[1]))
> {
> /* In the standard this is all evaluated inside of a consteval
> lambda. So, force in_immediate_context () around this. */
> in_consteval_if_p_temp_override icip;
> in_consteval_if_p = true;
> - tree i
> - = build_target_expr_with_type (begin,
> - cv_unqualified (TREE_TYPE (begin)),
> - tf_warning_or_error);
> + tree b = exprs[0], e = exprs[1];
> + if (TREE_CODE (b) == TARGET_EXPR)
> + b = TARGET_EXPR_INITIAL (b);
> + if (TREE_CODE (e) == TARGET_EXPR)
> + e = TARGET_EXPR_INITIAL (e);
> + b = force_target_expr (cv_unqualified (TREE_TYPE (b)), b,
> + tf_warning_or_error);
> + e = force_target_expr (cv_unqualified (TREE_TYPE (e)), e,
> + tf_warning_or_error);
Please add a comment about why you strip and re-add TARGET_EXPR.
OK with that addition.
Jason
@@ -8028,10 +8028,10 @@ 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_build_range_for_decls (location_t, tree, tree *, tree);
extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
tree, bool);
-extern tree build_range_temp (tree, bool = false);
+extern tree build_range_temp (tree, tree = NULL_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 &,
@@ -15854,16 +15854,17 @@ cp_parser_range_for (cp_parser *parser,
builds up the range temporary. */
tree
-build_range_temp (tree range_expr, bool expansion_stmt_p /* = false */)
+build_range_temp (tree range_expr, tree expansion_stmt_decl /* = NULL_TREE */)
{
tree range_type, auto_node;
- if (expansion_stmt_p)
+ if (expansion_stmt_decl)
{
/* Build const decltype(auto) __range = range_expr;
- range_expr provided by the caller already is (range_expr). */
- auto_node = make_decltype_auto ();
- range_type = cp_build_qualified_type (auto_node, TYPE_QUAL_CONST);
+ range_type = auto_node = make_decltype_auto ();
+ if (DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl))
+ range_type = cp_build_qualified_type (auto_node, TYPE_QUAL_CONST);
}
else
{
@@ -16007,11 +16008,13 @@ warn_for_range_copy (tree decl, tree exp
/* 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. */
+ __begin VAR_DECL, set *END_P to the __end VAR_DECL. If
+ EXPANSION_STMT_DECL, don't create __end and instead store
+ begin_expr to END_P[0] and end_expr to END_P[1]. */
tree
cp_build_range_for_decls (location_t loc, tree range_expr, tree *end_p,
- bool expansion_stmt_p)
+ tree expansion_stmt_decl)
{
tree iter_type, begin_expr, end_expr;
@@ -16023,30 +16026,34 @@ cp_build_range_for_decls (location_t loc
{
tree range_temp;
- if (!expansion_stmt_p
+ if (!expansion_stmt_decl
&& 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
{
- if (expansion_stmt_p)
+ if (expansion_stmt_decl)
{
/* Build constexpr decltype(auto) __for_range = (range_expr); */
location_t range_loc = cp_expr_loc_or_loc (range_expr, loc);
range_expr
= finish_parenthesized_expr (cp_expr (range_expr, range_loc));
- range_temp = build_range_temp (range_expr, true);
+ range_temp = build_range_temp (range_expr, expansion_stmt_decl);
/* When P2686R4 is fully implemented, these 3 sets of TREE_STATIC
(on range_temp, begin and end) should be removed as per
- CWG3044. */
- 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;
+ CWG3044. If expansion_stmt_decl is not constexpr, we don't
+ need the static though. */
+ if (DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl))
+ {
+ 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;
+ }
}
else
/* Build auto &&__for_range = range_expr; */
@@ -16063,12 +16070,14 @@ cp_build_range_for_decls (location_t loc
}
/* The new for initialization statement. */
- if (expansion_stmt_p && !TYPE_REF_P (iter_type))
+ if (expansion_stmt_decl
+ && DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl)
+ && !TYPE_REF_P (iter_type))
iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST);
tree begin = build_decl (loc, VAR_DECL, for_begin__identifier, iter_type);
TREE_USED (begin) = 1;
DECL_ARTIFICIAL (begin) = 1;
- if (expansion_stmt_p)
+ if (expansion_stmt_decl && DECL_DECLARED_CONSTEXPR_P (expansion_stmt_decl))
{
TREE_STATIC (begin) = 1;
DECL_DECLARED_CONSTEXPR_P (begin) = 1;
@@ -16079,21 +16088,18 @@ cp_build_range_for_decls (location_t loc
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
- if (cxx_dialect >= cxx17)
+ if (expansion_stmt_decl)
{
- iter_type = cv_unqualified (TREE_TYPE (end_expr));
- if (expansion_stmt_p && !TYPE_REF_P (iter_type))
- iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST);
+ end_p[0] = begin_expr;
+ end_p[1] = end_expr;
+ return begin;
}
+
+ 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,
@@ -16149,7 +16155,7 @@ cp_convert_range_for (tree statement, tr
if (range_decl == error_mark_node)
range_expr = error_mark_node;
tree begin
- = cp_build_range_for_decls (input_location, range_expr, &end, false);
+ = cp_build_range_for_decls (input_location, range_expr, &end, NULL_TREE);
finish_init_stmt (statement);
@@ -33329,34 +33329,46 @@ finish_expansion_stmt (tree expansion_st
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 exprs[2];
+ begin = cp_build_range_for_decls (loc, expansion_init, exprs, range_decl);
+ if (!error_operand_p (begin)
+ && !error_operand_p (exprs[0])
+ && !error_operand_p (exprs[1]))
{
/* In the standard this is all evaluated inside of a consteval
lambda. So, force in_immediate_context () around this. */
in_consteval_if_p_temp_override icip;
in_consteval_if_p = true;
- tree i
- = build_target_expr_with_type (begin,
- cv_unqualified (TREE_TYPE (begin)),
- tf_warning_or_error);
+ tree b = exprs[0], e = exprs[1];
+ if (TREE_CODE (b) == TARGET_EXPR)
+ b = TARGET_EXPR_INITIAL (b);
+ if (TREE_CODE (e) == TARGET_EXPR)
+ e = TARGET_EXPR_INITIAL (e);
+ b = force_target_expr (cv_unqualified (TREE_TYPE (b)), b,
+ tf_warning_or_error);
+ e = force_target_expr (cv_unqualified (TREE_TYPE (e)), e,
+ tf_warning_or_error);
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,
+ tree binc = build_x_unary_op (loc, PREINCREMENT_EXPR,
+ TARGET_EXPR_SLOT (b), 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,
+ WHILE_BODY (w) = build_compound_expr (loc, binc, rinc);
+ WHILE_COND (w) = build_x_binary_op (loc, NE_EXPR, b, ERROR_MARK,
+ e, 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));
+ {
+ warning_sentinel wur (warn_unused_result);
+ e = build_compound_expr (loc, b, e);
+ e = build_compound_expr (loc, r, e);
+ e = build_compound_expr (loc, e, w);
+ e = build_compound_expr (loc, e, TARGET_EXPR_SLOT (r));
+ }
+ e = fold_build_cleanup_point_expr (TREE_TYPE (e), e);
e = cxx_constant_value (e);
if (tree_fits_uhwi_p (e))
n = tree_to_uhwi (e);
@@ -33481,13 +33493,18 @@ finish_expansion_stmt (tree expansion_st
tf_warning_or_error);
auto_node = make_auto ();
iter_type = do_auto_deduction (auto_node, iter_init, auto_node);
- if (!TYPE_REF_P (iter_type))
+ if (DECL_DECLARED_CONSTEXPR_P (range_decl)
+ && !TYPE_REF_P (iter_type))
iter_type = cp_build_qualified_type (iter_type, TYPE_QUAL_CONST);
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;
+ if (DECL_DECLARED_CONSTEXPR_P (range_decl))
+ {
+ TREE_STATIC (iter) = 1;
+ DECL_DECLARED_CONSTEXPR_P (iter) = 1;
+ TREE_READONLY (iter) = 1;
+ }
pushdecl (iter);
cp_finish_decl (iter, iter_init, /*is_constant_init*/false,
NULL_TREE, LOOKUP_ONLYCONVERTING);
@@ -9,7 +9,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]; }
+ constexpr const S *end () const { return &c[s]; } // { dg-error "is not usable in a constant expression" "" { target c++11_down } }
int s;
};
struct V { int a; long b; double c; };
@@ -29,7 +29,7 @@ foo ()
template for (auto g : u) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
decltype(g)::type h = g;
- }
+ } // { dg-message "was not declared 'constexpr'" "" { target c++11_down } }
V v = { 9, 10L, 11.0 };
template for (auto g : v) // { dg-warning "'template for' only available with" "" { target c++23_down } }
{
@@ -1,5 +1,6 @@
// C++26 P1306R5 - Expansion statements
-// { dg-do run { target c++11 } }
+// { dg-do run { target c++14 } }
+// { dg-do compile { target c++11_only } }
// { dg-options "" }
namespace std {
@@ -11,7 +12,7 @@ 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]; }
+ constexpr const S *end () const { return &c[s]; } // { dg-error "is not usable in a constant expression" "" { target c++11_down } }
int s;
};
constexpr U u1 = { 3 }, u2 = { 0 };
@@ -45,7 +46,7 @@ foo ()
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 ();
+ __builtin_abort (); // { dg-message "was not declared 'constexpr'" "" { target c++11_down } }
return r;
}
@@ -49,12 +49,11 @@ foo ()
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 c++14 } .-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 }
- // { dg-error "the type 'const C' of 'constexpr' variable '__for_range ' is not literal" "" { target c++11_down } .-3 }
+ // { dg-error "the type 'const C' of 'constexpr' variable '__for_range ' is not literal" "" { 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 c++14 } .-1 }
- // { dg-error "call to non-'constexpr' function 'const A\\\* D::end\\\(\\\) const'" "" { target *-*-* } .-1 }
+ // { dg-error "call to non-'constexpr' function 'const A\\\* D::end\\\(\\\) const'" "" { target c++11_down } .-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 c++14 } .-1 }
@@ -1,5 +1,6 @@
// C++26 P1306R5 - Expansion statements
-// { dg-do run { target c++11 } }
+// { dg-do run { target c++14 } }
+// { dg-do compile { target c++11_only } }
// { dg-options "" }
namespace std {
@@ -18,7 +19,7 @@ template<int I> struct std::tuple_elemen
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]; }
+ constexpr const V *end () const { return &c[s]; } // { dg-error "is not usable in a constant expression" "" { target c++11_down } }
int s;
};
constexpr U u1 = { 3 }, u2 = { 0 };
@@ -51,6 +52,7 @@ foo ()
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 }
+ // { dg-message "was not declared 'constexpr'" "" { target c++11_down } .-3 }
return r;
}
@@ -22,6 +22,8 @@ namespace N
void
foo ()
{
- template for (auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ template for (constexpr auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
; // { dg-error "no match for 'operator-' in '__for_begin - __for_begin ' \\\(operand types are 'const A' and 'const A'\\\)" "" { target *-*-* } .-1 }
+ template for (auto i : N::B {}) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ ; // { dg-error "no match for 'operator-' in '__for_begin - __for_begin ' \\\(operand types are 'A' and 'A'\\\)" "" { target *-*-* } .-1 }
}
@@ -30,7 +30,7 @@ foo ()
for (auto i : N::S {}) // { dg-error "call of overloaded 'begin\\\(N::S\\\&\\\)' is ambiguous" }
;
template for (auto i : N::S {})
- ; // { dg-error "call of overloaded 'begin\\\(const N::S\\\&\\\)' is ambiguous" }
+ ; // { dg-error "call of overloaded 'begin\\\(N::S\\\&\\\)' is ambiguous" }
template for (auto i : V {})
; // { dg-error "cannot be used as a function" }
template for (auto i : O::B {})
@@ -0,0 +1,60 @@
+// CWG3140 - Allowing expansion over non-constant std::array
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+struct A
+{
+ int x;
+ constexpr explicit A (int v) : x(v) {}
+ constexpr A &operator ++ () { ++x; return *this; }
+ constexpr int operator * () const { return x; }
+ constexpr bool operator != (const A &o) const { return x != o.x; }
+ constexpr A operator + (int o) const { A r (x + o); return r; }
+ constexpr int operator - (const A &o) const { return x - o.x; }
+};
+
+namespace N
+{
+ struct B { constexpr B (int n) : b (n) {} int b; };
+ constexpr A begin (const B &) { return A (0); }
+ constexpr A end (const B &x) { return A (x.b); } // { dg-error "is not usable in a constant expression" }
+ struct C { };
+ constexpr A begin (const C &) { return A (0); }
+ constexpr A end (const C &x) { return A (6); }
+}
+
+int
+foo ()
+{
+ int r = 0;
+ template for (constexpr auto m : N::B (3)) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ r += m;
+ return r;
+}
+
+int
+bar ()
+{
+ int r = 0;
+ template for (auto m : N::B (3)) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ r += m; // { dg-message "was not declared 'constexpr'" }
+ return r;
+}
+
+int
+baz ()
+{
+ int r = 0;
+ template for (constexpr auto m : N::C ()) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ r += m;
+ return r;
+}
+
+int
+qux ()
+{
+ int r = 0;
+ template for (auto m : N::C ()) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ r += m;
+ return r;
+}
@@ -0,0 +1,21 @@
+// CWG3140 - Allowing expansion over non-constant std::array
+// { dg-do run { target c++17 } }
+// { dg-options "" }
+
+#include <array>
+
+std::array <int, 5>
+foo ()
+{
+ return { 1, 2, 3, 4, 5 };
+}
+
+int
+main ()
+{
+ int r = 0;
+ template for (auto n : foo ()) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ r += n;
+ if (r != 15)
+ __builtin_abort ();
+}
@@ -0,0 +1,19 @@
+// CWG3140 - Allowing expansion over non-constant std::array
+// { dg-do compile { target c++17 } }
+// { dg-options "" }
+
+#include <array>
+
+std::array <int, 5>
+foo ()
+{
+ return { 1, 2, 3, 4, 5 };
+}
+
+void
+bar ()
+{
+ int r = 0;
+ template for (constexpr auto n : foo ()) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ r += n; // { dg-error " call to non-'constexpr' function" "" { target *-*-* } .-1 }
+}
@@ -0,0 +1,36 @@
+// CWG3140 - Allowing expansion over non-constant std::array
+// { dg-do run { target c++17 } }
+// { dg-options "" }
+// { dg-additional-options "-frange-for-ext-temps" { target c++20_down } }
+
+#include <array>
+
+struct A {
+ A () { a++; }
+ A (const A &) { a++; }
+ ~A () { a--; }
+ static int a;
+};
+int A::a = 0;
+
+std::array <int, 5>
+foo (A, A, A)
+{
+ return { 1, 2, 3, 4, 5 };
+}
+
+int
+main ()
+{
+ int r = 0;
+ if (A::a != 0)
+ __builtin_abort ();
+ template for (auto n : foo (A (), A (), A ())) // { dg-warning "'template for' only available with" "" { target c++23_down } }
+ {
+ if (A::a != 3)
+ __builtin_abort ();
+ r += n;
+ }
+ if (r != 15 || A::a != 0)
+ __builtin_abort ();
+}