]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Fix up expansion statement handling
authorJakub Jelinek <jakub@redhat.com>
Fri, 19 Dec 2025 09:12:06 +0000 (10:12 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 19 Dec 2025 09:12:06 +0000 (10:12 +0100)
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  <jakub@redhat.com>

* 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.

gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp26/expansion-stmt1.C
gcc/testsuite/g++.dg/cpp26/expansion-stmt18.C
gcc/testsuite/g++.dg/cpp26/expansion-stmt2.C
gcc/testsuite/g++.dg/cpp26/expansion-stmt3.C

index 284fd9a534bc56a1bc96dc53c98ea890269b91b8..e106583c4b8bf0b9a7789f7956bbbf61e063db11 100644 (file)
@@ -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;
index ccbc6eb5410a90923b42dc1028b590c1cdbcc38d..ae7429b449c4cb7d1defbbe00eabec1f15e17abf 100644 (file)
@@ -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)
index 077b70c743c09161f456609e12f0ee9001510340..20a7413c14577dd072476a0f5998c96aa8337f1d 100644 (file)
@@ -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
index a3e7dd7684c685b99b82515b96c57ca7fd49d02e..ce0b39f5b6a2923297ce2b05e2b3670fc8b38f18 100644 (file)
@@ -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
index 590638e88e51011271a27c00178815ef5bf97fa2..89f6cce2670c8eea4a669bddc0decb19d4e7850c 100644 (file)
@@ -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
index b4b16bb0a2a9f25553a15983a28c78d80cddae9d..5103b3420f5e292bd8e5d68b08f8ac5d67102e9a 100644 (file)
@@ -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