2026-09-11 Jakub Jelinek <[email protected]>
P3598R0
PR c++/125840
* reflect.cc: Implement C++26 CWG 3158 - const-ification of Splice
Expressions.
(splice): Remember REFLECT_EXPR_KIND of REFLECT_EXPR. Set refl to
REFLECT_EXPR_HANDLE earlier. For REFLECT_OBJECT wrap decls into
a VCE to differentiate it from corresponding variables.
* pt.cc (tsubst_splice_expr): Call constify_contract_access if
needed.
* g++.dg/reflect/contract1.C: New test.
* g++.dg/reflect/contract2.C: New test.
* g++.dg/reflect/contract3.C: New test.
* g++.dg/reflect/contract4.C: New test.
* g++.dg/reflect/contract5.C: New test.
* g++.dg/reflect/reflect_object2.C (g): Add test for setting of
splice expr of an object reflection.
--- a/gcc/cp/reflect.cc 2026-08-20 08:39:02.400320178 +0200
+++ b/gcc/cp/reflect.cc 2026-09-10 17:55:59.978985838 +0200
@@ -8782,12 +8782,14 @@ splice (tree refl)
return error_mark_node;
}
+ reflect_kind kind = REFLECT_EXPR_KIND (refl);
+ refl = REFLECT_EXPR_HANDLE (refl);
+
/* We are bringing some entity from the unevaluated expressions world
to possibly outside of that, mark it used. */
- if (!mark_used (REFLECT_EXPR_HANDLE (refl)))
+ if (!mark_used (refl))
return error_mark_node;
- refl = REFLECT_EXPR_HANDLE (refl);
/* Function templates are wrapped in OVERLOAD from name lookup
and a lot of places assume that. Furthermore, if reflection comes
from ^^fntmpl, it is wrapped with OVERLOAD already, only when
@@ -8799,6 +8801,10 @@ splice (tree refl)
class for the access path. */
if (is_overloaded_fn (refl))
refl = baselink_for_fns (refl, /*ignore_current_class_p=*/true);
+ /* For [:reflect_object(var):] wrap var into a VCE, so that it is an lvalue,
+ but not the var itself. */
+ if (kind == REFLECT_OBJECT && DECL_P (refl))
+ refl = build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (refl), refl);
return refl;
}
--- a/gcc/cp/pt.cc 2026-09-10 15:13:06.631450280 +0200
+++ b/gcc/cp/pt.cc 2026-09-10 16:10:14.607111179 +0200
@@ -17174,7 +17174,11 @@ tsubst_splice_expr (tree t, tree args, t
if (SPLICE_EXPR_MEMBER_ACCESS_P (t))
gcc_assert (valid_splice_for_member_access_p (op,
/*decls_only_p=*/false));
else if (SPLICE_EXPR_EXPRESSION_P (t))
- op = convert_from_reference (op);
+ {
+ op = convert_from_reference (op);
+ if (flag_contracts && processing_contract_condition)
+ op = constify_contract_access (op);
+ }
return op;
}
--- a/gcc/testsuite/g++.dg/reflect/contract1.C 2026-09-10 15:13:14.746781204
+0200
+++ b/gcc/testsuite/g++.dg/reflect/contract1.C 2026-09-10 16:14:24.660891506
+0200
@@ -0,0 +1,32 @@
+// C++26 P3598R0 - CWG 3158 - const-ification of Splice Expressions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+namespace std
+{
+ template<typename T>
+ struct add_lvalue_reference
+ { using type = __add_lvalue_reference (T); };
+ template<typename T>
+ using add_lvalue_reference_t = typename add_lvalue_reference <T>::type;
+}
+
+#define UNCONST(x) const_cast <std::add_lvalue_reference_t <decltype (x)>> (x)
+
+int v;
+
+void
+foo (int p)
+ pre ((++p, true)) // { dg-error "increment of read-only location
'\\\(const int\\\)p'" }
+ pre ((++[:^^p:], true)) // { dg-error "increment of read-only location
'\\\(const int\\\)p'" }
+ pre ((++UNCONST (p), true))
+{
+}
+
+void
+bar ()
+ pre ((++v, true)) // { dg-error "increment of read-only location
'\\\(const int\\\)v'" }
+ pre ((++[:^^v:], true)) // { dg-error "increment of read-only location
'\\\(const int\\\)v'" }
+ pre ((++UNCONST (v), true))
+{
+}
--- a/gcc/testsuite/g++.dg/reflect/contract2.C 2026-09-10 15:13:14.746898983
+0200
+++ b/gcc/testsuite/g++.dg/reflect/contract2.C 2026-09-10 16:27:15.532724804
+0200
@@ -0,0 +1,71 @@
+// C++26 P3598R0 - CWG 3158 - const-ification of Splice Expressions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+namespace std
+{
+ template<typename T>
+ struct add_lvalue_reference
+ { using type = __add_lvalue_reference (T); };
+ template<typename T>
+ using add_lvalue_reference_t = typename add_lvalue_reference <T>::type;
+}
+
+#define UNCONST(x) const_cast <std::add_lvalue_reference_t <decltype (x)>> (x)
+
+template <typename T>
+void
+foo (T p) // { dg-error "increment of read-only location
'\\\(const int\\\)p'" }
+ pre ((++p, true))
+{
+}
+
+template <typename T>
+void
+bar (T p) // { dg-error "increment of read-only location
'\\\(const int\\\)p'" }
+ pre ((++[:^^p:], true))
+{
+}
+
+template <typename T>
+void
+baz (T p)
+ pre ((++UNCONST (p), true))
+{
+}
+
+int v; // { dg-error "increment of read-only location
'\\\(const int\\\)v'" }
+int w; // { dg-error "increment of read-only location
'\\\(const int\\\)w'" }
+int x;
+
+template <bool B>
+void
+corge (int p) // { dg-error "increment of read-only location
'\\\(const int\\\)p'" }
+ pre ((++(B ? v : p), true)) // { dg-error "increment of read-only location
'\\\(B \\\? \\\(\\\(const int\\\)v\\\) : \\\(\\\(const int\\\)p\\\)\\\)'" }
+{
+}
+
+template <bool B>
+void
+garply (int p)
+ pre ((++[:B ? ^^w : ^^p:], true))
+{
+}
+
+template <bool B>
+void
+waldo (int p)
+ pre ((++(B ? UNCONST (x) : UNCONST (p)), true))
+{
+}
+
+void
+qux (int p)
+{
+ foo (p); // { dg-message "required from here" }
+ bar (p); // { dg-message "required from here" }
+ baz (p);
+ corge <true> (p); // { dg-message "required from here" }
+ garply <true> (p); // { dg-message "required from here" }
+ waldo <true> (p);
+}
--- a/gcc/testsuite/g++.dg/reflect/contract3.C 2026-09-10 15:13:33.709088988
+0200
+++ b/gcc/testsuite/g++.dg/reflect/contract3.C 2026-09-10 16:30:30.859154428
+0200
@@ -0,0 +1,40 @@
+// C++26 P3598R0 - CWG 3158 - const-ification of Splice Expressions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+int v;
+
+void
+foo ()
+ pre ((++[:object_of (^^v):], true))
+{
+}
+
+void
+bar ()
+ pre ((++[:std::meta::reflect_object (v):], true))
+{
+}
+
+template <bool B>
+void
+baz (int p)
+ pre ((++[:object_of (B ? ^^v : ^^p):], true))
+{
+}
+
+template <bool B>
+void
+qux (int p)
+ pre ((++[:std::meta::reflect_object (B ? v : p):], true))
+{
+}
+
+void
+corge (int p)
+{
+ baz <true> (p);
+ qux <true> (p);
+}
--- a/gcc/testsuite/g++.dg/reflect/contract4.C 2026-09-10 16:31:10.752629451
+0200
+++ b/gcc/testsuite/g++.dg/reflect/contract4.C 2026-09-10 15:14:46.432118660
+0200
@@ -0,0 +1,39 @@
+// C++26 P3598R0 - CWG 3158 - const-ification of Splice Expressions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <array>
+#include <meta>
+#include <vector>
+
+template <typename T1, typename T2>
+auto
+foo (const T1 &t1, const T2 &t2)
+ pre([: is_integral_type (^^T1) ? ^^t2 : ^^t1 :]
+ [ [: is_integral_type (^^T1) ? ^^t1 : ^^t2 :] ] >= 0)
+{
+ constexpr auto key = is_integral_type (^^T1) ? ^^t1 : ^^t2;
+ constexpr auto container = is_integral_type (^^T1) ? ^^t2 : ^^t1;
+ return [: container :][ [: key :] ];
+}
+
+void
+bar (std::vector <int> &v)
+{
+ foo (v, 5);
+ foo (5, v);
+}
+
+template <std::size_t N>
+void
+baz (std::array <int, N> &v)
+{
+ foo (v, 5);
+ foo (5, v);
+}
+
+void
+qux (std::array <int, 7> &v)
+{
+ baz (v);
+}
--- a/gcc/testsuite/g++.dg/reflect/contract5.C 2026-09-10 16:31:15.872562075
+0200
+++ b/gcc/testsuite/g++.dg/reflect/contract5.C 2026-09-10 15:35:07.447779324
+0200
@@ -0,0 +1,26 @@
+// C++26 P3598R0 - CWG 3158 - const-ification of Splice Expressions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <map>
+#include <meta>
+
+template <typename T1, typename T2>
+auto
+foo (const T1 &t1, const T2 &t2)
+ pre([: is_integral_type (^^T1) ? ^^t2 : ^^t1 :]
+ [ [: is_integral_type (^^T1) ? ^^t1 : ^^t2 :] ] >= 0) // { dg-error "no match for
'operator\\\[\\\]' in 't1\\\[t2\\\]' \\\(operand types are 'const std::map<int, int>' and
'const int'\\\)" }
+{ // { dg-error "no match for
'operator\\\[\\\]' in 't2\\\[t1\\\]' \\\(operand types are 'const std::map<int, int>' and 'const
int'\\\)" "" { target *-*-* } .-1 }
+ constexpr auto key = is_integral_type (^^T1) ? ^^t1 : ^^t2;
+ constexpr auto container = is_integral_type (^^T1) ? ^^t2 : ^^t1;
+ return [: container :][ [: key :] ]; // { dg-error "no match
for 'operator\\\[\\\]' in 't1\\\[t2\\\]' \\\(operand types are 'const std::map<int, int>'
and 'const int'\\\)" }
+} // { dg-error "no match for
'operator\\\[\\\]' in 't2\\\[t1\\\]' \\\(operand types are 'const std::map<int, int>' and 'const
int'\\\)" "" { target *-*-* } .-1 }
+
+void
+bar (std::map <int, int> &m)
+{
+ foo (m, 5);
+ foo (5, m);
+}
+
+// { dg-error "cannot bind rvalue reference of type 'std::map<int, int>::key_type\\\&\\\&' {aka
'int\\\&\\\&'} to lvalue of type 'const int'" "" { target *-*-* } 0 }
--- a/gcc/testsuite/g++.dg/reflect/reflect_object2.C 2026-03-27
10:17:16.140298005 +0100
+++ b/gcc/testsuite/g++.dg/reflect/reflect_object2.C 2026-09-10
15:53:51.973822831 +0200
@@ -112,4 +112,5 @@ g ()
{
int i = [:robj:];
[:rbar:](42);
+ [:robj:] += 42;
}
Jakub