Hi! 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. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2025-11-12 Jakub Jelinek <[email protected]> * parser.cc (cp_build_range_for_decls): If expansion_stmt_p, where we 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. --- gcc/cp/parser.cc.jj 2025-11-12 16:30:45.118505899 +0100 +++ gcc/cp/parser.cc 2025-11-12 18:02:33.319150429 +0100 @@ -16011,6 +16011,8 @@ cp_build_range_for_decls (location_t loc } /* 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; @@ -16026,7 +16028,11 @@ cp_build_range_for_decls (location_t loc 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; --- gcc/cp/pt.cc.jj 2025-11-12 16:30:45.122505843 +0100 +++ gcc/cp/pt.cc 2025-11-12 18:06:33.928814874 +0100 @@ -33084,7 +33084,10 @@ finish_expansion_stmt (tree expansion_st 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)); @@ -33132,7 +33135,10 @@ finish_expansion_stmt (tree expansion_st 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) @@ -33173,6 +33179,8 @@ finish_expansion_stmt (tree expansion_st 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); if (args) @@ -33200,6 +33208,8 @@ 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)) + 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; @@ -33225,10 +33235,14 @@ finish_expansion_stmt (tree expansion_st 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) --- gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C.jj 2025-09-16 16:28:02.445139215 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C 2025-11-12 17:49:40.393864561 +0100 @@ -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 --- gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C.jj 2025-09-16 16:28:02.445139215 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C 2025-11-12 17:50:29.198188461 +0100 @@ -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 --- gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C.jj 2025-09-16 16:28:02.445139215 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C 2025-11-12 17:52:19.013666495 +0100 @@ -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 --- gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C.jj 2025-09-16 16:28:02.445139215 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C 2025-11-12 17:52:39.233386191 +0100 @@ -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 Jakub
