https://gcc.gnu.org/g:0f5760d5992c15b94c3a395b92040fcf2a9a276a
commit r16-6265-g0f5760d5992c15b94c3a395b92040fcf2a9a276a Author: Jakub Jelinek <[email protected]> Date: Fri Dec 19 10:12:06 2025 +0100 c++: Fix up expansion statement handling I've noticed that in many spots of the expansion statement handling I've handled incorrectly the creation of VAR_DECLs which are constexpr in the spec (or can be constexpr when user writes it that way). All I've done was set DECL_DECLARED_CONSTEXPR_P and TREE_READONLY flags on the VAR_DECL, but haven't made sure the TREE_TYPE is const qualified as well (with the exception of references obviously). Haven't touched spots which are always references, e.g. when it is constexpr auto &&var etc. Fixing this revealed some problems: 1) one fixed by first hunk in pt.cc, where the i variable was created with get_target_expr and thus now is const as well and so operator++ on it doesn't work; used build_target_expr_with_type to make it non-const 2) several tests got it wrong and didn't actually support calling operator *, operator != and operator + on const objects; fixed by making those operators const qualified. 2025-12-19 Jakub Jelinek <[email protected]> * parser.cc (cp_build_range_for_decls): If expansion_stmt_p, where we are setting DECL_DECLARED_CONSTEXPR_P on begin/end, use const qualified iter_type. * pt.cc (finish_expansion_stmt): Use build_target_expr_with_type with cv_unqualified to create it instead of get_target_expr to make it non-const qualified. When creating VAR_DECLs with DECL_DECLARED_CONSTEXPR_P, make sure they have const qualified type unless they are references. * g++.dg/cpp26/expansion-stmt1.C (A::operator *, A::operator !=, A::operator +, C::operator *, C::operator !=, C::operator +): Add const qualification. * g++.dg/cpp26/expansion-stmt2.C (A::operator *, A::operator !=, A::operator +, C::operator *, C::operator !=, C::operator +): Likewise. * g++.dg/cpp26/expansion-stmt3.C (A::operator *, A::operator !=, A::operator +, C::operator *, C::operator !=, C::operator +): Likewise. * g++.dg/cpp26/expansion-stmt18.C (A::operator *, A::operator !=, A::operator +): Likewise. Diff: --- gcc/cp/parser.cc | 8 +++++++- gcc/cp/pt.cc | 20 +++++++++++++++++--- gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C | 12 ++++++------ gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C | 6 +++--- gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C | 12 ++++++------ gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C | 12 ++++++------ 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 284fd9a534bc..e106583c4b8b 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -15287,6 +15287,8 @@ cp_build_range_for_decls (location_t loc, tree range_expr, tree *end_p, } /* The new for initialization statement. */ + if (expansion_stmt_p && !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; @@ -15302,7 +15304,11 @@ cp_build_range_for_decls (location_t loc, tree range_expr, tree *end_p, LOOKUP_ONLYCONVERTING); if (cxx_dialect >= cxx17) - iter_type = cv_unqualified (TREE_TYPE (end_expr)); + { + 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); + } tree end = build_decl (loc, VAR_DECL, for_end__identifier, iter_type); TREE_USED (end) = 1; DECL_ARTIFICIAL (end) = 1; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index ccbc6eb5410a..ae7429b449c4 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -32851,7 +32851,10 @@ finish_expansion_stmt (tree expansion_stmt, tree args, begin = cp_build_range_for_decls (loc, expansion_init, &end, true); if (!error_operand_p (begin) && !error_operand_p (end)) { - tree i = get_target_expr (begin); + tree i + = build_target_expr_with_type (begin, + cv_unqualified (TREE_TYPE (begin)), + 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)); @@ -32899,7 +32902,10 @@ finish_expansion_stmt (tree expansion_stmt, tree args, destruct_decls.safe_grow (n, true); for (unsigned HOST_WIDE_INT i = 0; i < n; ++i) { - tree this_decl = build_decl (loc, VAR_DECL, NULL_TREE, make_auto ()); + tree this_type = make_auto (); + if (DECL_DECLARED_CONSTEXPR_P (decl)) + this_type = cp_build_qualified_type (this_type, TYPE_QUAL_CONST); + tree this_decl = build_decl (loc, VAR_DECL, NULL_TREE, this_type); TREE_USED (this_decl) = 1; DECL_ARTIFICIAL (this_decl) = 1; DECL_DECLARED_CONSTEXPR_P (this_decl) @@ -32940,6 +32946,8 @@ finish_expansion_stmt (tree expansion_stmt, tree args, tree type = TREE_TYPE (range_decl); if (args) type = tsubst (type, args, complain | tf_tst_ok, in_decl); + if (DECL_DECLARED_CONSTEXPR_P (range_decl) && !TYPE_REF_P (type)) + type = cp_build_qualified_type (type, TYPE_QUAL_CONST); tree decl = build_decl (loc, VAR_DECL, DECL_NAME (range_decl), type); DECL_ATTRIBUTES (decl) = DECL_ATTRIBUTES (range_decl); TREE_USED (decl) |= TREE_USED (range_decl); @@ -32969,6 +32977,8 @@ finish_expansion_stmt (tree expansion_stmt, tree args, 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)) + 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; @@ -32994,10 +33004,14 @@ finish_expansion_stmt (tree expansion_stmt, tree args, this_decomp.count = TREE_VEC_LENGTH (v) - 1; for (unsigned i = 0; i < this_decomp.count; ++i) { + tree this_type = make_auto (); + if (DECL_DECLARED_CONSTEXPR_P (decl)) + this_type = cp_build_qualified_type (this_type, + TYPE_QUAL_CONST); tree this_decl = build_decl (loc, VAR_DECL, DECL_NAME (TREE_VEC_ELT (v, i + 1)), - make_auto ()); + this_type); TREE_USED (this_decl) = 1; DECL_ARTIFICIAL (this_decl) = 1; DECL_ATTRIBUTES (this_decl) diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C index 077b70c743c0..20a7413c1457 100644 --- a/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C @@ -21,18 +21,18 @@ struct A int x; constexpr explicit A (int v) : x(v) {} constexpr A &operator ++ () { ++x; return *this; } - constexpr int operator * () { return x; } - constexpr bool operator != (const A &o) { return x != o.x; } - constexpr A operator + (int o) { A r (x + o); return r; } + 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; } }; struct C { int x, y, z; constexpr explicit C (int u, int v, int w) : x(u), y(v), z(w) {} constexpr C &operator ++ () { ++x; --y; ++z; return *this; } - constexpr C operator * () { return *this; } - constexpr bool operator != (const C &o) { return x != o.x || y != o.y || z != o.z; } - constexpr C operator + (int o) { C r (x + o, y - o, z + o); return r; } + constexpr C operator * () const { return *this; } + constexpr bool operator != (const C &o) const { return x != o.x || y != o.y || z != o.z; } + constexpr C operator + (int o) const { C r (x + o, y - o, z + o); return r; } }; namespace N diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C index a3e7dd7684c6..ce0b39f5b6a2 100644 --- a/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C @@ -8,9 +8,9 @@ struct A int x; constexpr explicit A (int v) : x(v) {} constexpr A &operator ++ () { ++x; return *this; } - constexpr int operator * () { return x; } - constexpr bool operator != (const A &o) { return x != o.x; } - constexpr A operator + (int o) { A r (x + o); return r; } + 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; } }; namespace N diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C index 590638e88e51..89f6cce2670c 100644 --- a/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C @@ -21,18 +21,18 @@ struct A int x; constexpr explicit A (int v) : x(v) {} constexpr A &operator ++ () { ++x; return *this; } - constexpr int operator * () { return x; } - constexpr bool operator != (const A &o) { return x != o.x; } - constexpr A operator + (int o) { A r (x + o); return r; } + 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; } }; struct C { int x, y, z; constexpr explicit C (int u, int v, int w) : x(u), y(v), z(w) {} constexpr C &operator ++ () { ++x; --y; ++z; return *this; } - constexpr C operator * () { return *this; } - constexpr bool operator != (const C &o) { return x != o.x || y != o.y || z != o.z; } - constexpr C operator + (int o) { C r (x + o, y - o, z + o); return r; } + constexpr C operator * () const { return *this; } + constexpr bool operator != (const C &o) const { return x != o.x || y != o.y || z != o.z; } + constexpr C operator + (int o) const { C r (x + o, y - o, z + o); return r; } }; namespace N diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C index b4b16bb0a2a9..5103b3420f5e 100644 --- a/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C @@ -21,18 +21,18 @@ struct A int x; constexpr explicit A (int v) : x(v) {} constexpr A &operator ++ () { ++x; return *this; } - constexpr int operator * () { return x; } - constexpr bool operator != (const A &o) { return x != o.x; } - constexpr A operator + (int o) { A r (x + o); return r; } + 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; } }; struct C { int x, y, z; constexpr explicit C (int u, int v, int w) : x(u), y(v), z(w) {} constexpr C &operator ++ () { ++x; --y; ++z; return *this; } - constexpr C operator * () { return *this; } - constexpr bool operator != (const C &o) { return x != o.x || y != o.y || z != o.z; } - constexpr C operator + (int o) { C r (x + o, y - o, z + o); return r; } + constexpr C operator * () const { return *this; } + constexpr bool operator != (const C &o) const { return x != o.x || y != o.y || z != o.z; } + constexpr C operator + (int o) const { C r (x + o, y - o, z + o); return r; } }; namespace N
