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 &,
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
{
/* 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;
{
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; */
}
/* 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;
/*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,
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);
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];
+ /* The begin-expr and end-expr expressions will be usually wrapped
+ in TARGET_EXPR if they return a class iterator. The b
+ and e artificial variables need to have cv-unqualified type
+ so that e.g. b can be incremented, so unwrap the TARGET_EXPRs
+ and force TARGET_EXPR with the cv-unqualified type which is
+ a hack replacement for a VAR_DECL in a lambda. */
+ 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);
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);
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; };
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 } }
{
// 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 {
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 };
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;
}
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 }
// 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 {
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 };
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;
}
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 }
}
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 {})
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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 ();
+}
--- /dev/null
+// 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 }
+}
--- /dev/null
+// 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 ();
+}