On Thu, Sep 10, 2026 at 05:52:18PM +0200, Jakub Jelinek wrote: > On Thu, Sep 10, 2026 at 11:50:01AM -0400, Marek Polacek wrote: > > > @@ -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 (VIEW_CONVERT_EXPR, TREE_TYPE (refl), refl); > > > > Should we give this VCE the location loc? > > I think we could. Will test adjusted patch.
This passed full bootstrap/regtest on x86_64-linux and i686-linux. 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
