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

Reply via email to