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

Reply via email to