On Mon, 11 May 2026, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/16.2?
>
> -- >8 --
> 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) <case REFLECT_EXPR>: Use
> compare_reflections.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/reflect/alias3.C: New test.
> * g++.dg/reflect/bases_of5.C: New test.
> ---
> gcc/cp/reflect.cc | 5 +-
> gcc/cp/tree.cc | 15 +--
> gcc/testsuite/g++.dg/reflect/alias3.C | 15 +++
> gcc/testsuite/g++.dg/reflect/bases_of5.C | 128 +++++++++++++++++++++++
> 4 files changed, 148 insertions(+), 15 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/reflect/alias3.C
> create mode 100644 gcc/testsuite/g++.dg/reflect/bases_of5.C
>
> diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> index ad4c77fab3e..f03ad900f71 100644
> --- a/gcc/cp/reflect.cc
> +++ b/gcc/cp/reflect.cc
> @@ -8940,8 +8940,11 @@ compare_reflections (tree lhs, tree 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))
> + if (typedef_variant_p (lhs) ^ typedef_variant_p (rhs))
> return false;
> + /* Even if two aliases alias the same type, they're not equal. */
> + else if (typedef_variant_p (lhs))
> + return lhs == rhs;
I think we could simplify these ifs to:
if (typedef_variant_p (lhs) || typedef_variant_p (rhs))
return lhs == rhs;
OK either way
> /* 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 <class _E> 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 <typename> struct allocator_traits;
> +} // namespace std
> +constexpr void *operator new(unsigned long, void *__p) { return __p; }
> +namespace std {
> +template <typename _Tp, typename... _Args>
> +constexpr void _Construct(_Tp *__p, _Args... __args) {
> + new (__p) _Tp(__args...);
> +}
> +template <typename _Tp> 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 <typename _Tp> struct allocator_traits<__new_allocator<_Tp>> {
> + using allocator_type = __new_allocator<_Tp>;
> + using value_type = _Tp;
> + using pointer = _Tp *;
> + template <typename _Up> 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 <typename _Alloc>
> +struct __alloc_traits : std::allocator_traits<_Alloc> {
> + typedef std::allocator_traits<_Alloc> _Base_type;
> + typedef _Base_type::value_type reference;
> + template <typename _Tp> struct rebind {
> + typedef _Base_type::template rebind_alloc<_Tp> other;
> + };
> +};
> +namespace std {
> +template <typename _InputIterator, typename _Sentinel,
> + typename _ForwardIterator>
> +constexpr void __do_uninit_copy(_InputIterator __first, _Sentinel __last,
> + _ForwardIterator __result) {
> + for (; __first != __last; ++__first, ++__result)
> + _Construct(__result, *__first);
> +}
> +template <typename _InputIterator, typename _ForwardIterator>
> +constexpr void uninitialized_copy(_InputIterator __first, _InputIterator
> __last,
> + _ForwardIterator __result) {
> + __do_uninit_copy(__first, __last, __result);
> +}
> +template <typename _InputIterator, typename _Sentinel,
> + typename _ForwardIterator, typename _Tp>
> +constexpr void __uninitialized_copy_a(_InputIterator __first, _Sentinel
> __last,
> + _ForwardIterator __result, _Tp) {
> + uninitialized_copy(__first, __last, __result);
> +}
> +template <typename _Tp, typename _Alloc> 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 <typename _Tp, typename _Alloc = __new_allocator<_Tp>>
> +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 <typename _Iterator, typename _Sentinel>
> + 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<info> bases_of(info, access_context);
> +} // namespace meta
> +} // namespace std
> +template <class L> using mp_is_value_list = L;
> +template <template <class> class, class...> long cx_count_if;
> +template <std::meta::info> struct rf_base_desc;
> +struct X1 {};
> +struct X2 {};
> +struct Z : X1, X2 {};
> +int main() {
> + constexpr auto all = std::meta::access_context();
> + using B1 = rf_base_desc<bases_of(^^Z, all)[0]>;
> + using B2 = rf_base_desc<bases_of(^^Z, all)[1]>;
> + (void) cx_count_if<mp_is_value_list, B1, B2>;
> +}
>
> base-commit: be1da01067c898a3e3979bfb1edd05f115ab2e3e
> --
> 2.54.0
>
>