From: Marek Polacek Date: Thu, 7 May 2026 20:38:34 +0000 (-0400) Subject: c++/reflection: fixes for comparing reflections [PR125208] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7199cf12773b1429c19cdd5b69950b2f09470e64;p=thirdparty%2Fgcc.git c++/reflection: fixes for comparing reflections [PR125208] This fixes two bugs: 1) crash in cp_tree_equal when comparing reflections with binfos; cp_tree_equal doesn't handle those. We're coming from lookup_template_class -> spec_hasher::equal -> comp_template_args -> cp_tree_equal. We should use compare_reflections in cp_tree_equal. 2) the fix for 1) revealed that compare_reflections is buggy when comparing two aliases: we shouldn't fall back to same_type_p because given using A = int; using B = int; ^^A != ^^B should hold. PR c++/125208 gcc/cp/ChangeLog: * reflect.cc (compare_reflections): Use == when comparing two aliases. * tree.cc (cp_tree_equal) : Use compare_reflections. gcc/testsuite/ChangeLog: * g++.dg/reflect/alias3.C: New test. * g++.dg/reflect/bases_of5.C: New test. Reviewed-by: Patrick Palka --- diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index c8148d96726..c30ba1582af 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -8948,9 +8948,12 @@ compare_reflections (tree lhs, tree rhs) return lhs == rhs; else if (TYPE_P (lhs) && TYPE_P (rhs)) { - /* Given "using A = int;", "^^int != ^^A" should hold. */ - if (typedef_variant_p (lhs) != typedef_variant_p (rhs)) - return false; + /* Given + using A = int; + using B = int; + ^^int != ^^A and ^^A != ^^B. */ + if (typedef_variant_p (lhs) || typedef_variant_p (rhs)) + return lhs == rhs; /* This is for comparing function types. E.g., auto fn() -> int; type_of(^^fn) == ^^auto()->int; */ return same_type_p (lhs, rhs); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 2e96d65eecd..02a62f78511 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -4514,20 +4514,7 @@ cp_tree_equal (tree t1, tree t2) return true; case REFLECT_EXPR: - { - if (REFLECT_EXPR_KIND (t1) != REFLECT_EXPR_KIND (t2)) - return false; - tree h1 = REFLECT_EXPR_HANDLE (t1); - tree h2 = REFLECT_EXPR_HANDLE (t2); - if (!cp_tree_equal (h1, h2)) - return false; - /* ^^alias represents the alias itself, not the underlying type. */ - if (TYPE_P (h1) - && (typedef_variant_p (h1) || typedef_variant_p (h2)) - && TYPE_NAME (h1) != TYPE_NAME (h2)) - return false; - return true; - } + return compare_reflections (t1, t2); default: break; diff --git a/gcc/testsuite/g++.dg/reflect/alias3.C b/gcc/testsuite/g++.dg/reflect/alias3.C new file mode 100644 index 00000000000..802c3231c88 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/alias3.C @@ -0,0 +1,15 @@ +// PR c++/125208 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +using A = int; +using B = int; +using C = A; +static_assert (^^A == ^^A); +static_assert (^^const A == ^^const A); +static_assert (^^A != ^^const A); +static_assert (^^B == ^^B); +static_assert (^^C == ^^C); +static_assert (^^A != ^^B); +static_assert (^^A != ^^C); +static_assert (^^B != ^^C); diff --git a/gcc/testsuite/g++.dg/reflect/bases_of5.C b/gcc/testsuite/g++.dg/reflect/bases_of5.C new file mode 100644 index 00000000000..1c713b22cda --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/bases_of5.C @@ -0,0 +1,128 @@ +// PR c++/125208 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +namespace std { +template struct initializer_list { + typedef _E *const_iterator; + _E *_M_array; + unsigned long _M_len; + constexpr long size() { return _M_len; } + constexpr const_iterator begin() { return _M_array; } + constexpr const_iterator end() { return begin() + size(); } +}; +template struct allocator_traits; +} // namespace std +constexpr void *operator new(unsigned long, void *__p) { return __p; } +namespace std { +template +constexpr void _Construct(_Tp *__p, _Args... __args) { + new (__p) _Tp(__args...); +} +template struct __new_allocator { + constexpr _Tp *allocate(long __n) { + return static_cast<_Tp *>(__builtin_operator_new(__n * sizeof(_Tp))); + } + constexpr void deallocate(_Tp *__p, long) { __builtin_operator_delete(__p); } +}; +template struct allocator_traits<__new_allocator<_Tp>> { + using allocator_type = __new_allocator<_Tp>; + using value_type = _Tp; + using pointer = _Tp *; + template using rebind_alloc = __new_allocator<_Up>; + static constexpr pointer allocate(allocator_type __a, long __n) { + return __a.allocate(__n); + } + static constexpr void deallocate(allocator_type __a, pointer __p, long __n) { + __a.deallocate(__p, __n); + } +}; +} // namespace std +template +struct __alloc_traits : std::allocator_traits<_Alloc> { + typedef std::allocator_traits<_Alloc> _Base_type; + typedef _Base_type::value_type reference; + template struct rebind { + typedef _Base_type::template rebind_alloc<_Tp> other; + }; +}; +namespace std { +template +constexpr void __do_uninit_copy(_InputIterator __first, _Sentinel __last, + _ForwardIterator __result) { + for (; __first != __last; ++__first, ++__result) + _Construct(__result, *__first); +} +template +constexpr void uninitialized_copy(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result) { + __do_uninit_copy(__first, __last, __result); +} +template +constexpr void __uninitialized_copy_a(_InputIterator __first, _Sentinel __last, + _ForwardIterator __result, _Tp) { + uninitialized_copy(__first, __last, __result); +} +template struct _Vector_base { + typedef __alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type; + typedef __alloc_traits<_Tp_alloc_type>::pointer pointer; + struct _Vector_impl : _Tp_alloc_type { + constexpr _Vector_impl(_Tp_alloc_type) {} + pointer _M_start; + }; + constexpr ~_Vector_base() { _M_deallocate(_M_impl._M_start); } + _Vector_impl _M_impl; + constexpr pointer _M_allocate(long __n) { + return __n ? __alloc_traits<_Tp_alloc_type>::allocate(_M_impl, __n) + : pointer(); + } + constexpr void _M_deallocate(pointer __p) { + __alloc_traits<_Tp_alloc_type>::deallocate(_M_impl, __p, 0); + } +}; +template > +struct vector : _Vector_base<_Tp, _Alloc> { + typedef _Vector_base<_Tp, _Alloc> _Base; + typedef _Base::_Tp_alloc_type _Tp_alloc_type; + typedef _Alloc allocator_type; + constexpr vector(initializer_list<_Tp> __l, + allocator_type __a = allocator_type()) + : _Base(__a) { + long __trans_tmp_1 = __l.size(); + _M_range_initialize_n(__l.begin(), __l.end(), __trans_tmp_1); + } + constexpr __alloc_traits<_Tp_alloc_type>::reference operator[](long __n) { + return *(this->_M_impl._M_start + __n); + } + template + constexpr void _M_range_initialize_n(_Iterator __first, _Sentinel __last, + long __n) { + typename _Base::pointer __start = this->_M_allocate(__n); + this->_M_impl._M_start = __start; + _Tp_alloc_type __trans_tmp_2; + __uninitialized_copy_a(__first, __last, __start, __trans_tmp_2); + } +}; +namespace meta { +using info = decltype(^^int); +struct access_context { + info _M_scope; + info _M_designating_class; +}; +consteval vector bases_of(info, access_context); +} // namespace meta +} // namespace std +template using mp_is_value_list = L; +template