Skip to content

[libc++][ranges] Adjust inheritance detection for enable_view #132582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

frederick-vs-ja
Copy link
Contributor

Per [range.view]/6, a view_interface isn't a base class of itself, so enable_view should report false. Also, current implementation strategy handles const but not volatile, IIUC cv-qualifiers should be consistent handled.

In enable_view.compile.pass.cpp, coverage for (const) volatile types are added.

Drive-by: Remove one unnessary test_macro.h inclusion in a test.

Fixes #132577.

Per [range.view]/6, a `view_interface` isn't a base class of itself, so
`enable_view` should report `false`. Also, current implementation
strategy handles `const` but not `volatile`, IIUC cv-qualifiers should
be consistent handled.

In `enable_view.compile.pass.cpp`, coverage for (`const`) `volatile`
types are added.

Drive-by: Remove one unnessary `test_macro.h` inclusion in a test.
@frederick-vs-ja frederick-vs-ja requested a review from a team as a code owner March 23, 2025 03:29
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 23, 2025

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

Changes

Per [range.view]/6, a view_interface isn't a base class of itself, so enable_view should report false. Also, current implementation strategy handles const but not volatile, IIUC cv-qualifiers should be consistent handled.

In enable_view.compile.pass.cpp, coverage for (const) volatile types are added.

Drive-by: Remove one unnessary test_macro.h inclusion in a test.

Fixes #132577.


Full diff: https://github.com/llvm/llvm-project/pull/132582.diff

3 Files Affected:

  • (modified) libcxx/include/__ranges/enable_view.h (+3-4)
  • (modified) libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp (+64)
  • (modified) libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp (+95-2)
diff --git a/libcxx/include/__ranges/enable_view.h b/libcxx/include/__ranges/enable_view.h
index f570926eb67c3..6dee6ea3fa3d2 100644
--- a/libcxx/include/__ranges/enable_view.h
+++ b/libcxx/include/__ranges/enable_view.h
@@ -14,7 +14,6 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__type_traits/is_class.h>
-#include <__type_traits/is_convertible.h>
 #include <__type_traits/remove_cv.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -34,12 +33,12 @@ template <class _Derived>
 class view_interface;
 
 template <class _Op, class _Yp>
-  requires is_convertible_v<_Op*, view_interface<_Yp>*>
-void __is_derived_from_view_interface(const _Op*, const view_interface<_Yp>*);
+  requires(!same_as<_Op, view_interface<_Yp>>)
+void __is_derived_from_view_interface(view_interface<_Yp>*);
 
 template <class _Tp>
 inline constexpr bool enable_view = derived_from<_Tp, view_base> || requires {
-  ranges::__is_derived_from_view_interface((_Tp*)nullptr, (_Tp*)nullptr);
+  ranges::__is_derived_from_view_interface<remove_cv_t<_Tp>>((remove_cv_t<_Tp>*)nullptr);
 };
 
 } // namespace ranges
diff --git a/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp
index 6412234dce048..202b5bba451ce 100644
--- a/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp
@@ -25,6 +25,12 @@ static_assert(!std::ranges::enable_view<Empty&&>);
 static_assert(!std::ranges::enable_view<const Empty>);
 static_assert(!std::ranges::enable_view<const Empty&>);
 static_assert(!std::ranges::enable_view<const Empty&&>);
+static_assert(!std::ranges::enable_view<volatile Empty>);
+static_assert(!std::ranges::enable_view<volatile Empty&>);
+static_assert(!std::ranges::enable_view<volatile Empty&&>);
+static_assert(!std::ranges::enable_view<const volatile Empty>);
+static_assert(!std::ranges::enable_view<const volatile Empty&>);
+static_assert(!std::ranges::enable_view<const volatile Empty&&>);
 
 // Derives from view_base, but privately
 struct PrivateViewBase : private std::ranges::view_base { };
@@ -34,6 +40,12 @@ static_assert(!std::ranges::enable_view<PrivateViewBase&&>);
 static_assert(!std::ranges::enable_view<const PrivateViewBase>);
 static_assert(!std::ranges::enable_view<const PrivateViewBase&>);
 static_assert(!std::ranges::enable_view<const PrivateViewBase&&>);
+static_assert(!std::ranges::enable_view<volatile PrivateViewBase>);
+static_assert(!std::ranges::enable_view<volatile PrivateViewBase&>);
+static_assert(!std::ranges::enable_view<volatile PrivateViewBase&&>);
+static_assert(!std::ranges::enable_view<const volatile PrivateViewBase>);
+static_assert(!std::ranges::enable_view<const volatile PrivateViewBase&>);
+static_assert(!std::ranges::enable_view<const volatile PrivateViewBase&&>);
 
 // Derives from view_base, but specializes enable_view to false
 struct EnableViewFalse : std::ranges::view_base { };
@@ -44,6 +56,12 @@ static_assert(!std::ranges::enable_view<EnableViewFalse&&>);
 static_assert(std::ranges::enable_view<const EnableViewFalse>);
 static_assert(!std::ranges::enable_view<const EnableViewFalse&>);
 static_assert(!std::ranges::enable_view<const EnableViewFalse&&>);
+static_assert(std::ranges::enable_view<volatile EnableViewFalse>);
+static_assert(!std::ranges::enable_view<volatile EnableViewFalse&>);
+static_assert(!std::ranges::enable_view<volatile EnableViewFalse&&>);
+static_assert(std::ranges::enable_view<const volatile EnableViewFalse>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewFalse&>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewFalse&&>);
 
 // Derives from view_base
 struct PublicViewBase : std::ranges::view_base { };
@@ -53,6 +71,12 @@ static_assert(!std::ranges::enable_view<PublicViewBase&&>);
 static_assert(std::ranges::enable_view<const PublicViewBase>);
 static_assert(!std::ranges::enable_view<const PublicViewBase&>);
 static_assert(!std::ranges::enable_view<const PublicViewBase&&>);
+static_assert(std::ranges::enable_view<volatile PublicViewBase>);
+static_assert(!std::ranges::enable_view<volatile PublicViewBase&>);
+static_assert(!std::ranges::enable_view<volatile PublicViewBase&&>);
+static_assert(std::ranges::enable_view<const volatile PublicViewBase>);
+static_assert(!std::ranges::enable_view<const volatile PublicViewBase&>);
+static_assert(!std::ranges::enable_view<const volatile PublicViewBase&&>);
 
 // Does not derive from view_base, but specializes enable_view to true
 struct EnableViewTrue { };
@@ -63,6 +87,12 @@ static_assert(!std::ranges::enable_view<EnableViewTrue&&>);
 static_assert(!std::ranges::enable_view<const EnableViewTrue>);
 static_assert(!std::ranges::enable_view<const EnableViewTrue&>);
 static_assert(!std::ranges::enable_view<const EnableViewTrue&&>);
+static_assert(!std::ranges::enable_view<volatile EnableViewTrue>);
+static_assert(!std::ranges::enable_view<volatile EnableViewTrue&>);
+static_assert(!std::ranges::enable_view<volatile EnableViewTrue&&>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewTrue>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewTrue&>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewTrue&&>);
 
 // Make sure that enable_view is a bool, not some other contextually-convertible-to-bool type.
 ASSERT_SAME_TYPE(decltype(std::ranges::enable_view<Empty>), const bool);
@@ -75,6 +105,12 @@ static_assert(!std::ranges::enable_view<V1&&>);
 static_assert(std::ranges::enable_view<const V1>);
 static_assert(!std::ranges::enable_view<const V1&>);
 static_assert(!std::ranges::enable_view<const V1&&>);
+static_assert(std::ranges::enable_view<volatile V1>);
+static_assert(!std::ranges::enable_view<volatile V1&>);
+static_assert(!std::ranges::enable_view<volatile V1&&>);
+static_assert(std::ranges::enable_view<const volatile V1>);
+static_assert(!std::ranges::enable_view<const volatile V1&>);
+static_assert(!std::ranges::enable_view<const volatile V1&&>);
 
 struct V2 : std::ranges::view_interface<V1>, std::ranges::view_interface<V2> {};
 static_assert(!std::ranges::enable_view<V2>);
@@ -83,6 +119,12 @@ static_assert(!std::ranges::enable_view<V2&&>);
 static_assert(!std::ranges::enable_view<const V2>);
 static_assert(!std::ranges::enable_view<const V2&>);
 static_assert(!std::ranges::enable_view<const V2&&>);
+static_assert(!std::ranges::enable_view<volatile V2>);
+static_assert(!std::ranges::enable_view<volatile V2&>);
+static_assert(!std::ranges::enable_view<volatile V2&&>);
+static_assert(!std::ranges::enable_view<const volatile V2>);
+static_assert(!std::ranges::enable_view<const volatile V2&>);
+static_assert(!std::ranges::enable_view<const volatile V2&&>);
 
 struct V3 : std::ranges::view_interface<V1> {};
 static_assert(std::ranges::enable_view<V3>);
@@ -91,13 +133,35 @@ static_assert(!std::ranges::enable_view<V3&&>);
 static_assert(std::ranges::enable_view<const V3>);
 static_assert(!std::ranges::enable_view<const V3&>);
 static_assert(!std::ranges::enable_view<const V3&&>);
+static_assert(std::ranges::enable_view<volatile V3>);
+static_assert(!std::ranges::enable_view<volatile V3&>);
+static_assert(!std::ranges::enable_view<volatile V3&&>);
+static_assert(std::ranges::enable_view<const volatile V3>);
+static_assert(!std::ranges::enable_view<const volatile V3&>);
+static_assert(!std::ranges::enable_view<const volatile V3&&>);
 
 struct PrivateInherit : private std::ranges::view_interface<PrivateInherit> {};
 static_assert(!std::ranges::enable_view<PrivateInherit>);
+static_assert(!std::ranges::enable_view<const PrivateInherit>);
+static_assert(!std::ranges::enable_view<volatile PrivateInherit>);
+static_assert(!std::ranges::enable_view<const volatile PrivateInherit>);
+
+// https://github.com/llvm/llvm-project/issues/132577
+// enable_view<view_interface<T>> should be false.
+static_assert(!std::ranges::enable_view<std::ranges::view_interface<V1>>);
+static_assert(!std::ranges::enable_view<const std::ranges::view_interface<V1>>);
+static_assert(!std::ranges::enable_view<volatile std::ranges::view_interface<V1>>);
+static_assert(!std::ranges::enable_view<const volatile std::ranges::view_interface<V1>>);
 
 // ADL-proof
 struct Incomplete;
 template<class T> struct Holder { T t; };
 static_assert(!std::ranges::enable_view<Holder<Incomplete>*>);
+static_assert(!std::ranges::enable_view<const Holder<Incomplete>*>);
+static_assert(!std::ranges::enable_view<volatile Holder<Incomplete>*>);
+static_assert(!std::ranges::enable_view<const volatile Holder<Incomplete>*>);
 
 static_assert(!std::ranges::enable_view<void>);
+static_assert(!std::ranges::enable_view<const void>);
+static_assert(!std::ranges::enable_view<volatile void>);
+static_assert(!std::ranges::enable_view<const volatile void>);
diff --git a/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp
index e7a7f30cb20fb..38828cbe46b5b 100644
--- a/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp
@@ -15,8 +15,6 @@
 
 #include <ranges>
 
-#include "test_macros.h"
-
 // The type would be a view, but it's not moveable.
 struct NotMoveable : std::ranges::view_base {
   NotMoveable() = default;
@@ -90,3 +88,98 @@ static_assert(std::movable<View>);
 static_assert(std::default_initializable<View>);
 static_assert(std::ranges::enable_view<View>);
 static_assert(std::ranges::view<View>);
+
+// const view types
+
+struct ConstView1 : std::ranges::view_base {
+  ConstView1(const ConstView1&&);
+  const ConstView1& operator=(const ConstView1&&) const;
+
+  friend void swap(const ConstView1&, const ConstView1&);
+
+  friend int* begin(const ConstView1&);
+  friend int* end(const ConstView1&);
+};
+static_assert(std::ranges::range<const ConstView1>);
+static_assert(std::movable<const ConstView1>);
+static_assert(!std::default_initializable<const ConstView1>);
+static_assert(std::ranges::enable_view<const ConstView1>);
+static_assert(std::ranges::view<const ConstView1>);
+
+struct ConstView2 : std::ranges::view_interface<ConstView2> {
+  ConstView2(const ConstView2&&);
+  const ConstView2& operator=(const ConstView2&&) const;
+
+  friend void swap(const ConstView2&, const ConstView2&);
+
+  friend int* begin(const ConstView2&);
+  friend int* end(const ConstView2&);
+};
+static_assert(std::ranges::range<const ConstView2>);
+static_assert(std::movable<const ConstView2>);
+static_assert(!std::default_initializable<const ConstView2>);
+static_assert(std::ranges::enable_view<const ConstView2>);
+static_assert(std::ranges::view<const ConstView2>);
+
+// volatile view types
+struct VolatileView1 : std::ranges::view_base {
+  VolatileView1(volatile VolatileView1&&);
+  volatile VolatileView1& operator=(volatile VolatileView1&&) volatile;
+
+  friend void swap(volatile VolatileView1&, volatile VolatileView1&);
+
+  friend int* begin(volatile VolatileView1&);
+  friend int* end(volatile VolatileView1&);
+};
+static_assert(std::ranges::range<volatile VolatileView1>);
+static_assert(std::movable<volatile VolatileView1>);
+static_assert(!std::default_initializable<volatile VolatileView1>);
+static_assert(std::ranges::enable_view<volatile VolatileView1>);
+static_assert(std::ranges::view<volatile VolatileView1>);
+
+struct VolatileView2 : std::ranges::view_interface<VolatileView2> {
+  VolatileView2(volatile VolatileView2&&);
+  volatile VolatileView2& operator=(volatile VolatileView2&&) volatile;
+
+  friend void swap(volatile VolatileView2&, volatile VolatileView2&);
+
+  friend int* begin(volatile VolatileView2&);
+  friend int* end(volatile VolatileView2&);
+};
+static_assert(std::ranges::range<volatile VolatileView2>);
+static_assert(std::movable<volatile VolatileView2>);
+static_assert(!std::default_initializable<volatile VolatileView2>);
+static_assert(std::ranges::enable_view<volatile VolatileView2>);
+static_assert(std::ranges::view<volatile VolatileView2>);
+
+// const-volatile view types
+
+struct ConstVolatileView1 : std::ranges::view_base {
+  ConstVolatileView1(const volatile ConstVolatileView1&&);
+  const volatile ConstVolatileView1& operator=(const volatile ConstVolatileView1&&) const volatile;
+
+  friend void swap(const volatile ConstVolatileView1&, const volatile ConstVolatileView1&);
+
+  friend int* begin(const volatile ConstVolatileView1&);
+  friend int* end(const volatile ConstVolatileView1&);
+};
+static_assert(std::ranges::range<const volatile ConstVolatileView1>);
+static_assert(std::movable<const volatile ConstVolatileView1>);
+static_assert(!std::default_initializable<const volatile ConstVolatileView1>);
+static_assert(std::ranges::enable_view<const volatile ConstVolatileView1>);
+static_assert(std::ranges::view<const volatile ConstVolatileView1>);
+
+struct ConstVolatileView2 : std::ranges::view_interface<ConstVolatileView2> {
+  ConstVolatileView2(const volatile ConstVolatileView2&&);
+  const volatile ConstVolatileView2& operator=(const volatile ConstVolatileView2&&) const volatile;
+
+  friend void swap(const volatile ConstVolatileView2&, const volatile ConstVolatileView2&);
+
+  friend int* begin(const volatile ConstVolatileView2&);
+  friend int* end(const volatile ConstVolatileView2&);
+};
+static_assert(std::ranges::range<const volatile ConstVolatileView2>);
+static_assert(std::movable<const volatile ConstVolatileView2>);
+static_assert(!std::default_initializable<const volatile ConstVolatileView2>);
+static_assert(std::ranges::enable_view<const volatile ConstVolatileView2>);
+static_assert(std::ranges::view<const volatile ConstVolatileView2>);

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix, LGTM!

@ldionne ldionne merged commit 3c0300d into llvm:main Mar 27, 2025
83 checks passed
@frederick-vs-ja frederick-vs-ja deleted the enable_view-volatile-view_inteface branch March 27, 2025 01:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] view_interface is not view
3 participants