]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/reflection: fixes for comparing reflections [PR125208]
authorMarek Polacek <polacek@redhat.com>
Thu, 7 May 2026 20:38:34 +0000 (16:38 -0400)
committerMarek Polacek <polacek@redhat.com>
Tue, 12 May 2026 00:32:42 +0000 (20:32 -0400)
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.

Reviewed-by: Patrick Palka <ppalka@redhat.com>
gcc/cp/reflect.cc
gcc/cp/tree.cc
gcc/testsuite/g++.dg/reflect/alias3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/reflect/bases_of5.C [new file with mode: 0644]

index c8148d9672632ef9177d6d455bc0e5c9b72a4343..c30ba1582af4ee1decac4f5f880e328f86014445 100644 (file)
@@ -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);
index 2e96d65eecd74aefb1f62f39f3af6064fdf764b7..02a62f78511cf55a31b74a733ffd3b2169c1b954 100644 (file)
@@ -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 (file)
index 0000000..802c323
--- /dev/null
@@ -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 (file)
index 0000000..1c713b2
--- /dev/null
@@ -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>;
+}