[clang] [Clang] Fix another concept cache bug (PR #162272)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 7 06:11:47 PDT 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/162272
>From b44ac2ace2beb4650d3acf93bdf7599f23a98b75 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 7 Oct 2025 20:16:05 +0800
Subject: [PATCH 1/2] [Clang] Fix another concept cache bug
We previously failed to profile dependent CallExprs; this patch corrects
that behavior. We now also profile canonical types whenever possible,
since type sugar has little impact on hash computation.
---
clang/lib/Sema/SemaConcept.cpp | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 8946f1bfc7a95..f4df63c1d2243 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -305,6 +305,12 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
if (!NTTP)
return TraverseDecl(D);
+ if (NTTP->getDepth() >= TemplateArgs.getNumLevels())
+ return true;
+
+ if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(), NTTP->getIndex()))
+ return true;
+
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) {
assert(Arg.getKind() == TemplateArgument::Pack &&
@@ -331,17 +337,25 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
return inherited::TraverseDecl(D);
}
+ bool TraverseCallExpr(CallExpr *CE) {
+ inherited::TraverseStmt(CE->getCallee());
+
+ for (Expr *Arg : CE->arguments())
+ inherited::TraverseStmt(Arg);
+
+ return true;
+ }
+
bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
// We don't care about TypeLocs. So traverse Types instead.
- return TraverseType(TL.getType(), TraverseQualifier);
+ return TraverseType(TL.getType().getCanonicalType(), TraverseQualifier);
}
bool TraverseTagType(const TagType *T, bool TraverseQualifier) {
// T's parent can be dependent while T doesn't have any template arguments.
// We should have already traversed its qualifier.
// FIXME: Add an assert to catch cases where we failed to profile the
- // concept. assert(!T->isDependentType() && "We missed a case in profiling
- // concepts!");
+ // concept.
return true;
}
@@ -706,7 +720,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
-
auto &Cached = Iter->second.Satisfaction;
Satisfaction.ContainsErrors = Cached.ContainsErrors;
Satisfaction.IsSatisfied = Cached.IsSatisfied;
>From de3340a0e3c14dfe4721f68ec427e8eaa56f5c6e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 7 Oct 2025 21:11:19 +0800
Subject: [PATCH 2/2] Add test - we cannot reduce it further
---
clang/test/SemaCXX/GH161671.cpp | 339 ++++++++++++++++++++++++++++++++
1 file changed, 339 insertions(+)
create mode 100644 clang/test/SemaCXX/GH161671.cpp
diff --git a/clang/test/SemaCXX/GH161671.cpp b/clang/test/SemaCXX/GH161671.cpp
new file mode 100644
index 0000000000000..de09e548d91f2
--- /dev/null
+++ b/clang/test/SemaCXX/GH161671.cpp
@@ -0,0 +1,339 @@
+// RUN: %clang_cc1 -std=c++20 -w %s
+// RUN: %clang_cc1 -std=c++2c -w %s
+// expected-no-diagnostics
+
+namespace std {
+template <typename _Tp, _Tp __v> struct integral_constant {
+ static constexpr _Tp value = __v;
+ using value_type = _Tp;
+};
+template <bool __v> using __bool_constant = integral_constant<bool, __v>;
+template <typename> struct is_integral : integral_constant<bool, true> {};
+template <typename> struct is_signed : integral_constant<bool, false> {};
+template <typename _Tp, typename _Up = _Tp> _Up __declval(int);
+template <typename _Tp> auto declval() -> decltype(__declval<_Tp>(0));
+template <typename> struct make_unsigned {
+ using type = int;
+};
+template <typename _Tp> struct decay {
+ using type = _Tp;
+};
+template <int, typename _Iftrue, typename> struct conditional {
+ using type = _Iftrue;
+};
+} // namespace std
+namespace meta {
+template <template <typename...> class> struct quote;
+template <template <typename> class C, typename... Ts>
+concept valid = requires { typename C<Ts...>; };
+template <typename T>
+concept trait = requires { typename T; };
+template <typename T>
+concept invocable = requires { typename quote<T::template invoke>; };
+template <typename T>
+concept integral = requires { T::value; };
+template <trait T> using _t = T::type;
+template <integral T> constexpr T::value_type _v = T::value;
+template <bool B> using bool_ = std::integral_constant<bool, B>;
+template <invocable Fn, typename... Args>
+using invoke = Fn::template invoke<Args...>;
+template <typename> struct id;
+namespace detail {
+template <template <typename> class, typename...> struct defer_;
+template <template <typename> class C, typename... Ts>
+ requires valid<C, Ts...>
+struct defer_<C, Ts...> {
+ using type = C<Ts...>;
+};
+} // namespace detail
+template <template <typename> class C, typename... Ts>
+struct defer : detail::defer_<C, Ts...> {};
+template <template <typename...> class C> struct quote {
+ template <typename... Ts> using invoke = _t<defer<C, Ts...>>;
+};
+namespace detail {
+template <int> struct _cond {
+ template <typename Then, typename> using invoke = Then;
+};
+template <> struct _cond<false>;
+} // namespace detail
+template <bool If, typename Then, typename Else>
+using conditional_t = detail::_cond<If>::template invoke<Then, Else>;
+namespace detail {
+template <typename...> struct _if_;
+template <typename If, typename Then, typename Else>
+struct _if_<If, Then, Else> : std::conditional<_v<If>, Then, Else> {};
+} // namespace detail
+template <bool If, typename... Args>
+using if_c = _t<detail::_if_<bool_<If>, Args...>>;
+} // namespace meta
+template <bool> void requires_();
+template <typename A, typename B>
+concept same_as = __is_same(B, A);
+namespace ranges {
+template <typename> struct view_closure;
+template <typename T> using decay_t = meta::_t<std::decay<T>>;
+enum cardinality { unknown };
+template <cardinality> struct basic_view {};
+} // namespace ranges
+namespace std {
+template <typename> struct vector {};
+} // namespace std
+namespace ranges {
+struct {
+ template <typename F, typename... Args>
+ auto operator()(F f, Args... args) -> decltype(f(args...));
+} invoke;
+template <typename Fun, typename... Args>
+using invoke_result_t =
+ decltype(invoke(std::declval<Fun>(), std::declval<Args>()...));
+namespace detail {
+struct with_difference_type_;
+template <typename T> using iter_value_t_ = T ::value_type;
+} // namespace detail
+template <typename R> using iter_value_t = detail::iter_value_t_<R>;
+namespace detail {
+template <typename I>
+using iter_size_t =
+ meta::_t<meta::conditional_t<std::is_integral<I>::value,
+ std::make_unsigned<I>, meta::id<I>>>;
+template <typename D>
+concept signed_integer_like_impl_concept_ =
+ std::integral_constant<bool, -D()>::value;
+template <typename D>
+concept signed_integer_like_ = signed_integer_like_impl_concept_<D>;
+} // namespace detail
+template <typename S, typename I>
+concept sized_sentinel_for_requires_ =
+ requires(S s, I i) { requires_<same_as<I, decltype(i - s)>>; };
+template <typename S, typename I>
+concept sized_sentinel_for = sized_sentinel_for_requires_<S, I>;
+struct range_access {
+ template <typename Rng>
+ static auto begin_cursor(Rng rng) -> decltype(rng.begin_cursor());
+ template <typename Cur, typename O>
+ static auto distance_to(Cur pos, O other) -> decltype(pos.distance_to(other));
+};
+namespace detail {
+template <typename S, typename C>
+concept sized_sentinel_for_cursor_requires_ = requires(S s, C c) {
+ requires_<signed_integer_like_<decltype(range_access::distance_to(c, s))>>;
+};
+template <typename S, typename C>
+concept sized_sentinel_for_cursor = sized_sentinel_for_cursor_requires_<S, C>;
+struct iterator_associated_types_base_ {
+ typedef range_access value_type;
+};
+template <typename>
+using iterator_associated_types_base = iterator_associated_types_base_;
+} // namespace detail
+template <typename>
+struct basic_iterator : detail::iterator_associated_types_base<int> {};
+template <typename Cur2, typename Cur>
+ requires detail::sized_sentinel_for_cursor<Cur2, Cur>
+void operator-(basic_iterator<Cur2>, basic_iterator<Cur>);
+namespace _begin_ {
+template <typename T>
+concept has_member_begin_requires_ = requires(T t) { t; };
+template <typename T>
+concept has_member_begin = has_member_begin_requires_<T>;
+struct _member_result_ {
+ template <typename R>
+ using invoke = decltype(static_cast<R (*)()>(nullptr)().begin());
+};
+struct _non_member_result_;
+struct fn {
+ template <typename R>
+ using _result_t =
+ meta::invoke<meta::conditional_t<has_member_begin<R>, _member_result_,
+ _non_member_result_>,
+ R>;
+ template <typename R> _result_t<R> operator()(R);
+};
+} // namespace _begin_
+_begin_::fn begin;
+namespace _end_ {
+template <typename>
+concept has_member_end_requires_ = requires { begin; };
+template <typename T>
+concept has_member_end = has_member_end_requires_<T>;
+struct _member_result_ {
+ template <typename R>
+ using invoke = decltype(static_cast<R (*)()>(nullptr)().end());
+};
+struct _non_member_result_;
+struct fn {
+ template <typename R>
+ using _result_t =
+ meta::invoke<meta::conditional_t<has_member_end<R>, _member_result_,
+ _non_member_result_>,
+ R>;
+ template <typename R> _result_t<R> operator()(R);
+};
+} // namespace _end_
+_end_::fn end;
+template <typename Rng>
+using iterator_t = decltype(begin(static_cast<Rng (*)()>(nullptr)()));
+template <typename Rng>
+using sentinel_t = decltype(end(static_cast<Rng (*)()>(nullptr)()));
+template <typename T>
+concept has_member_size_requires_ = requires(T t) { t.size(); };
+template <typename T>
+concept has_member_size = has_member_size_requires_<T>;
+struct _other_result_;
+struct _member_result_ {
+ template <typename> using invoke = decltype(0);
+ template <typename R>
+ using _result_t = meta::invoke<
+ meta::conditional_t<has_member_size<R>, _member_result_, _other_result_>,
+ R>;
+ template <typename R> _result_t<R> operator()(R r) { r.size(); }
+} size;
+template <typename Rng> using range_value_t = iter_value_t<iterator_t<Rng>>;
+namespace detail {
+template <cardinality Card>
+std::integral_constant<cardinality, Card> test_cardinality(basic_view<Card> *);
+}
+template <typename Rng>
+struct range_cardinality
+ : meta::conditional_t<__is_same(Rng, Rng),
+ decltype(detail::test_cardinality(
+ static_cast<Rng *>(nullptr))),
+ Rng> {};
+template <typename T>
+concept sized_range_requires_ = requires(T t) { size(t); };
+template <typename T>
+concept sized_range = sized_range_requires_<T>;
+namespace detail {
+template <int> struct dependent_ {
+ template <typename T> using invoke = T;
+};
+} // namespace detail
+template <typename Derived, cardinality Cardinality>
+struct view_interface : basic_view<Cardinality> {
+ template <bool B> using D = meta::invoke<detail::dependent_<B>, Derived>;
+ Derived derived();
+ template <bool True = true>
+ requires sized_sentinel_for<sentinel_t<D<True>>, iterator_t<D<True>>>
+ detail::iter_size_t<iterator_t<D<True>>> size() {
+ derived().end() - derived().begin();
+ }
+};
+struct {
+ template <typename Fun> view_closure<Fun> operator()(Fun);
+} make_view_closure;
+struct view_closure_base {
+ template <typename Rng, typename ViewFn>
+ friend auto operator|(Rng rng, ViewFn vw) {
+ return vw(rng);
+ }
+};
+template <typename ViewFn> struct view_closure : view_closure_base, ViewFn {};
+namespace detail {
+template <typename Derived>
+using begin_cursor_t =
+ decay_t<decltype(range_access::begin_cursor(std::declval<Derived>()))>;
+template <typename Derived>
+using facade_iterator_t = basic_iterator<begin_cursor_t<Derived>>;
+template <typename Derived>
+using facade_sentinel_t =
+ meta::if_c<same_as<Derived, Derived>, facade_iterator_t<Derived>, Derived>;
+} // namespace detail
+template <typename Derived, cardinality Cardinality>
+struct view_facade : view_interface<Derived, Cardinality> {
+ template <typename D = Derived> auto begin() -> detail::facade_iterator_t<D>;
+ template <typename D = Derived> auto end() -> detail::facade_sentinel_t<D>;
+};
+template <typename Derived, cardinality Cardinality>
+struct view_adaptor : view_facade<Derived, Cardinality> {
+ auto begin_cursor() -> decltype(0);
+};
+namespace detail {
+template <typename...> struct bind_back_fn_;
+template <typename Fn, typename Arg> struct bind_back_fn_<Fn, Arg> {
+ template <typename... CallArgs>
+ invoke_result_t<Fn, CallArgs..., Arg> operator()(CallArgs...);
+};
+template <typename Fn, typename... Args>
+using bind_back_fn = bind_back_fn_<Fn, Args...>;
+} // namespace detail
+struct {
+ template <typename Fn, typename Arg1>
+ detail::bind_back_fn<Fn, Arg1> operator()(Fn, Arg1);
+} bind_back;
+namespace detail {
+struct to_container {
+ template <typename> struct fn;
+ template <typename, typename> struct closure;
+};
+template <typename, typename, typename R>
+concept to_container_reserve = sized_range<R>;
+template <typename MetaFn, typename Rng>
+using container_t = meta::invoke<MetaFn, Rng>;
+struct to_container_closure_base {
+ template <typename Rng, typename MetaFn, typename Fn>
+ friend auto operator|(Rng rng, to_container::closure<MetaFn, Fn> fn) {
+ return fn(rng);
+ }
+};
+template <typename, typename Fn>
+struct to_container::closure : to_container_closure_base, Fn {};
+template <typename MetaFn> struct to_container::fn {
+ template <typename Rng> void impl(Rng, std::__bool_constant<false>);
+ template <typename Rng> void impl(Rng rng, std::__bool_constant<true>) {
+ size(rng);
+ }
+ template <typename Rng> container_t<MetaFn, Rng> operator()(Rng rng) {
+ using cont_t = container_t<MetaFn, Rng>;
+ using iter_t = Rng;
+ using use_reserve_t =
+ meta::bool_<to_container_reserve<cont_t, iter_t, Rng>>;
+ impl(rng, use_reserve_t{});
+ }
+};
+template <typename MetaFn, typename Fn>
+using to_container_closure = to_container::closure<MetaFn, Fn>;
+template <typename MetaFn>
+using to_container_fn = to_container_closure<MetaFn, to_container::fn<MetaFn>>;
+template <template <typename> class ContT> struct from_range {
+ template <typename Rng>
+ static auto from_rng_(long)
+ -> meta::invoke<meta::quote<ContT>, range_value_t<Rng>>;
+ template <typename Rng> using invoke = decltype(from_rng_<Rng>(0));
+};
+} // namespace detail
+detail::to_container_fn<detail::from_range<std::vector>> to_vector;
+template <typename Rng>
+struct remove_if_view
+ : view_adaptor<remove_if_view<Rng>, range_cardinality<Rng>::value> {};
+struct filter_base_fn {
+ template <typename Rng, typename Pred>
+ remove_if_view<Rng> operator()(Rng, Pred);
+ template <typename Pred> auto operator()(Pred pred) {
+ return make_view_closure(bind_back(filter_base_fn{}, pred));
+ }
+} filter;
+namespace detail {
+struct promote_as_signed_;
+template <typename I>
+using iota_difference_t =
+ meta::conditional_t<std::is_integral<I>::value, promote_as_signed_,
+ with_difference_type_>;
+} // namespace detail
+template <typename, typename>
+struct iota_view : view_facade<iota_view<int, int>, unknown> {
+ struct cursor {
+ auto distance_to(cursor) -> detail::iota_difference_t<int>;
+ };
+ cursor begin_cursor();
+};
+struct {
+ template <typename From, typename To>
+ requires(std::is_signed<From>::value == std::is_signed<To>::value)
+ iota_view<From, To> operator()(From, To);
+} iota;
+} // namespace ranges
+void foo() {
+ ranges::iota(0, 1) | ranges::to_vector =
+ ranges::iota(0, 1) | ranges::filter([] {}) | ranges::to_vector;
+}
More information about the cfe-commits
mailing list