[clang] [Clang] Profile the NNS of UnresolvedUsingType correctly in concept hashing (PR #199617)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 1 03:51:35 PDT 2026
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/199617
>From dd85ecb9ba4217f2d49746c97da3478d14d9d513 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 26 May 2026 15:12:52 +0800
Subject: [PATCH] [Clang] Profile the NNS of UnresolvedUsingType correctly in
concept hashing
They were sometimes incorrect because the written type doesn't contain an
NNS which contains template parameters we're interested in.
---
clang/lib/Sema/SemaConcept.cpp | 14 +++
.../test/SemaTemplate/concepts-using-decl.cpp | 98 +++++++++++++++++++
2 files changed, 112 insertions(+)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index ea03c3f408986..f41f37b8a3d19 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -381,6 +381,10 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
return true;
}
+ bool TraverseCXXThisExpr(CXXThisExpr *E) {
+ return inherited::TraverseType(E->getType());
+ }
+
bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
// We don't care about TypeLocs. So traverse Types instead.
return TraverseType(TL.getType().getCanonicalType(), TraverseQualifier);
@@ -399,6 +403,16 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
return true;
}
+ bool TraverseUnresolvedUsingType(UnresolvedUsingType *T,
+ bool TraverseQualifier) {
+ // Sometimes the written type doesn't contain a qualifier which contains
+ // necessary template arguments, whereas the declaration does.
+ if (NestedNameSpecifier NNS = T->getDecl()->getQualifier();
+ TraverseQualifier && NNS)
+ return inherited::TraverseNestedNameSpecifier(NNS);
+ return inherited::TraverseUnresolvedUsingType(T, TraverseQualifier);
+ }
+
bool TraverseInjectedClassNameType(InjectedClassNameType *T,
bool TraverseQualifier) {
return TraverseTemplateArguments(T->getTemplateArgs(SemaRef.Context));
diff --git a/clang/test/SemaTemplate/concepts-using-decl.cpp b/clang/test/SemaTemplate/concepts-using-decl.cpp
index 26bd0b60b691c..00df6b899782c 100644
--- a/clang/test/SemaTemplate/concepts-using-decl.cpp
+++ b/clang/test/SemaTemplate/concepts-using-decl.cpp
@@ -197,3 +197,101 @@ struct child : base<int> {
};
}
+
+namespace GH198663 {
+
+template <class T>
+concept HasIsTransparent = requires { typename T::is_transparent; };
+
+template <class K, class V, class Compare>
+struct FlatMapBase {
+ using key_compare = Compare;
+};
+
+template <class K, class V, class Compare>
+struct FlatMap : FlatMapBase<K, V, Compare> {
+ using Base = FlatMapBase<K, V, Compare>;
+
+ using typename Base::key_compare;
+
+ void at(const K&) {}
+ void at(const K&) const {}
+ template <class Other>
+ void at(const Other&)
+ requires HasIsTransparent<key_compare>
+ {}
+ template <class Other>
+ void at(const Other&) const
+ requires HasIsTransparent<key_compare>
+ {}
+};
+
+template <class T>
+struct Transparent {
+ T t;
+};
+
+struct TransparentComparator {
+ using is_transparent = void;
+
+ template <class T>
+ bool operator()(const T&, const Transparent<T>&) const;
+
+ template <class T>
+ bool operator()(const Transparent<T>&, const T& t) const;
+
+ template <class T>
+ bool operator()(const T&, const T&) const;
+};
+
+struct NonTransparentComparator {
+ template <class T>
+ bool operator()(const T&, const Transparent<T>&) const;
+
+ template <class T>
+ bool operator()(const Transparent<T>&, const T&) const;
+
+ template <class T>
+ bool operator()(const T&, const T&) const;
+};
+
+template <class M>
+concept CanAt = requires(M m, Transparent<int> k) { m.at(k); };
+
+using TransparentMap = FlatMap<int, double, TransparentComparator>;
+using NonTransparentMap = FlatMap<int, double, NonTransparentComparator>;
+
+static_assert(CanAt<TransparentMap>);
+
+static_assert(!CanAt<NonTransparentMap>);
+
+}
+
+namespace GH198663_2 {
+
+template<typename T>
+auto mv(T& t) -> T&&;
+
+template<typename S, typename T>
+concept does_foo = requires(S s) {
+ s.template foo<T>();
+};
+
+template<typename S>
+struct type {
+ S member;
+ template<typename T>
+ auto foo() -> T requires does_foo<decltype(mv(member)), T>;
+};
+
+struct returns_int {
+ template<typename T>
+ auto foo() -> T;
+};
+
+struct nothing {};
+
+static_assert(does_foo<type<returns_int>&, int>);
+static_assert(not does_foo<type<nothing>&, int>);
+
+}
More information about the cfe-commits
mailing list