[libcxx-commits] [libcxx] [libc++] Replace __is_primary_template with a concept (PR #197646)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu May 14 03:07:56 PDT 2026
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/197646
`__is_primary_template` is only used in C++20 and later, so we can replace it with a concept, which is significantly faster than the current SFINAE machinery. In my testig this reduces the time to instantiate `std::ranges::find_first_of` from ~48ms to ~40ms.
>From 360992fc4166b8a46422c0f366ae3e5ff2f50ad6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 14 May 2026 12:06:16 +0200
Subject: [PATCH] [libc++] Replace __is_primary_template with a concept
---
libcxx/include/CMakeLists.txt | 2 +-
.../primary_template.h} | 12 ++++--------
libcxx/include/__iterator/concepts.h | 15 +++++++--------
libcxx/include/__iterator/incrementable_traits.h | 4 ++--
libcxx/include/__iterator/iterator_traits.h | 4 ++--
libcxx/include/__iterator/readable_traits.h | 2 +-
libcxx/include/module.modulemap.in | 5 +----
7 files changed, 18 insertions(+), 26 deletions(-)
rename libcxx/include/{__type_traits/is_primary_template.h => __concepts/primary_template.h} (66%)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5c2520de90dcc..499a33f304b12 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -323,6 +323,7 @@ set(files
__concepts/invocable.h
__concepts/movable.h
__concepts/predicate.h
+ __concepts/primary_template.h
__concepts/regular.h
__concepts/relation.h
__concepts/same_as.h
@@ -875,7 +876,6 @@ set(files
__type_traits/is_pod.h
__type_traits/is_pointer.h
__type_traits/is_polymorphic.h
- __type_traits/is_primary_template.h
__type_traits/is_reference.h
__type_traits/is_reference_wrapper.h
__type_traits/is_referenceable.h
diff --git a/libcxx/include/__type_traits/is_primary_template.h b/libcxx/include/__concepts/primary_template.h
similarity index 66%
rename from libcxx/include/__type_traits/is_primary_template.h
rename to libcxx/include/__concepts/primary_template.h
index 5fe6820bc7f7a..31f79dd8e20d8 100644
--- a/libcxx/include/__type_traits/is_primary_template.h
+++ b/libcxx/include/__concepts/primary_template.h
@@ -10,9 +10,6 @@
#define _LIBCPP___TYPE_TRAITS_IS_PRIMARY_TEMPLATE_H
#include <__config>
-#include <__type_traits/enable_if.h>
-#include <__type_traits/is_same.h>
-#include <__type_traits/is_valid_expansion.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -21,11 +18,10 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
-using __test_for_primary_template _LIBCPP_NODEBUG =
- __enable_if_t<_IsSame<_Tp, typename _Tp::__primary_template>::value>;
-
-template <class _Tp>
-using __is_primary_template _LIBCPP_NODEBUG = _IsValidExpansion<__test_for_primary_template, _Tp>;
+concept __primary_template = requires {
+ typename _Tp::__primary_template;
+ requires __is_same(typename _Tp::__primary_template, _Tp);
+};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__iterator/concepts.h b/libcxx/include/__iterator/concepts.h
index 3b43920443636..a3f07e8032888 100644
--- a/libcxx/include/__iterator/concepts.h
+++ b/libcxx/include/__iterator/concepts.h
@@ -20,6 +20,7 @@
#include <__concepts/invocable.h>
#include <__concepts/movable.h>
#include <__concepts/predicate.h>
+#include <__concepts/primary_template.h>
#include <__concepts/regular.h>
#include <__concepts/relation.h>
#include <__concepts/same_as.h>
@@ -38,10 +39,8 @@
#include <__type_traits/integral_constant.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_primary_template.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_referenceable.h>
-#include <__type_traits/is_valid_expansion.h>
#include <__type_traits/remove_cv.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/forward.h>
@@ -80,7 +79,7 @@ template <class _Tp>
concept __specialization_of_projected = requires {
typename __projected_iterator_t<_Tp>;
typename __projected_projection_t<_Tp>;
-} && __is_primary_template<_Tp>::value;
+} && __primary_template<_Tp>;
template <class _Tp>
struct __indirect_value_t_impl {
@@ -153,8 +152,7 @@ concept sized_sentinel_for =
template <class _Iter>
struct __iter_traits_cache {
- using type _LIBCPP_NODEBUG =
- _If<__is_primary_template<iterator_traits<_Iter> >::value, _Iter, iterator_traits<_Iter> >;
+ using type _LIBCPP_NODEBUG = _If<__primary_template<iterator_traits<_Iter> >, _Iter, iterator_traits<_Iter> >;
};
template <class _Iter>
using _ITER_TRAITS _LIBCPP_NODEBUG = typename __iter_traits_cache<_Iter>::type;
@@ -169,12 +167,13 @@ struct __iter_concept_category_test {
};
struct __iter_concept_random_fallback {
template <class _Iter>
- using _Apply _LIBCPP_NODEBUG =
- __enable_if_t<__is_primary_template<iterator_traits<_Iter> >::value, random_access_iterator_tag>;
+ using _Apply _LIBCPP_NODEBUG = __enable_if_t<__primary_template<iterator_traits<_Iter> >, random_access_iterator_tag>;
};
template <class _Iter, class _Tester>
-struct __test_iter_concept : _IsValidExpansion<_Tester::template _Apply, _Iter>, _Tester {};
+ struct __test_iter_concept : bool_constant < requires {
+ typename _Tester::template _Apply<_Iter>;
+} >, _Tester{};
template <class _Iter>
struct __iter_concept_cache {
diff --git a/libcxx/include/__iterator/incrementable_traits.h b/libcxx/include/__iterator/incrementable_traits.h
index 37c8daddf8a86..dc62b1abb397a 100644
--- a/libcxx/include/__iterator/incrementable_traits.h
+++ b/libcxx/include/__iterator/incrementable_traits.h
@@ -11,11 +11,11 @@
#define _LIBCPP___ITERATOR_INCREMENTABLE_TRAITS_H
#include <__concepts/arithmetic.h>
+#include <__concepts/primary_template.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
#include <__type_traits/conditional.h>
#include <__type_traits/is_object.h>
-#include <__type_traits/is_primary_template.h>
#include <__type_traits/make_signed.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/declval.h>
@@ -68,7 +68,7 @@ struct iterator_traits;
// generated from the primary template, and `iterator_traits<RI>::difference_type` otherwise.
template <class _Ip>
using iter_difference_t =
- typename conditional_t<__is_primary_template<iterator_traits<remove_cvref_t<_Ip> > >::value,
+ typename conditional_t<__primary_template<iterator_traits<remove_cvref_t<_Ip> > >,
incrementable_traits<remove_cvref_t<_Ip> >,
iterator_traits<remove_cvref_t<_Ip> > >::difference_type;
diff --git a/libcxx/include/__iterator/iterator_traits.h b/libcxx/include/__iterator/iterator_traits.h
index ebf315a53b6b7..db7228297fc73 100644
--- a/libcxx/include/__iterator/iterator_traits.h
+++ b/libcxx/include/__iterator/iterator_traits.h
@@ -15,6 +15,7 @@
#include <__concepts/convertible_to.h>
#include <__concepts/copyable.h>
#include <__concepts/equality_comparable.h>
+#include <__concepts/primary_template.h>
#include <__concepts/same_as.h>
#include <__concepts/totally_ordered.h>
#include <__config>
@@ -30,7 +31,6 @@
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_object.h>
-#include <__type_traits/is_primary_template.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_referenceable.h>
#include <__type_traits/nat.h>
@@ -468,7 +468,7 @@ using __iterator_reference _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::re
// This has to be in this file and not readable_traits.h to break the include cycle between the two.
template <class _Ip>
using iter_value_t =
- typename conditional_t<__is_primary_template<iterator_traits<remove_cvref_t<_Ip> > >::value,
+ typename conditional_t<__primary_template<iterator_traits<remove_cvref_t<_Ip> > >,
indirectly_readable_traits<remove_cvref_t<_Ip> >,
iterator_traits<remove_cvref_t<_Ip> > >::value_type;
diff --git a/libcxx/include/__iterator/readable_traits.h b/libcxx/include/__iterator/readable_traits.h
index 25e74567fff11..05c24d828937c 100644
--- a/libcxx/include/__iterator/readable_traits.h
+++ b/libcxx/include/__iterator/readable_traits.h
@@ -10,12 +10,12 @@
#ifndef _LIBCPP___ITERATOR_READABLE_TRAITS_H
#define _LIBCPP___ITERATOR_READABLE_TRAITS_H
+#include <__concepts/primary_template.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__type_traits/conditional.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_object.h>
-#include <__type_traits/is_primary_template.h>
#include <__type_traits/remove_cv.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_extent.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b5aa319d0aca2..02d54d27c64ca 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -255,10 +255,6 @@ module std_core [system] {
header "__type_traits/is_polymorphic.h"
export std_core.type_traits.integral_constant
}
- module is_primary_template {
- header "__type_traits/is_primary_template.h"
- export std_core.type_traits.integral_constant
- }
module is_reference_wrapper {
header "__type_traits/is_reference_wrapper.h"
export std_core.type_traits.integral_constant
@@ -1089,6 +1085,7 @@ module std [system] {
module invocable { header "__concepts/invocable.h" }
module movable { header "__concepts/movable.h" }
module predicate { header "__concepts/predicate.h" }
+ module primary_template { header "__concepts/primary_template.h" }
module regular { header "__concepts/regular.h" }
module relation { header "__concepts/relation.h" }
module same_as { header "__concepts/same_as.h" }
More information about the libcxx-commits
mailing list