[clang] [Clang] Fix a concept subsumption bug when template depths are adjusted (PR #186735)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 16 04:36:14 PDT 2026
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/186735
>From d9166d1e298b6e464e0b7da445d3dc7ac158e36d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 16 Mar 2026 12:49:49 +0800
Subject: [PATCH 1/2] [Clang] Fix a concept subsumption bug when template
depths are adjusted
We cannot reuse the cached normalization results if any template depth
adjustments (in subsumption checking) are involved.
---
clang/include/clang/Sema/SemaConcept.h | 3 ++-
clang/lib/Sema/SemaConcept.cpp | 11 +++++++----
clang/test/SemaTemplate/concepts.cpp | 19 +++++++++++++++++++
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index bdd997b18cb08..e6644f1bd1bee 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -431,7 +431,8 @@ class SubsumptionChecker {
std::optional<bool> Subsumes(const NamedDecl *DP,
ArrayRef<AssociatedConstraint> P,
const NamedDecl *DQ,
- ArrayRef<AssociatedConstraint> Q);
+ ArrayRef<AssociatedConstraint> Q,
+ bool DepthAdjusted);
bool Subsumes(const NormalizedConstraint *P, const NormalizedConstraint *Q);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 38791940247cb..16cbec22c823b 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2544,7 +2544,8 @@ bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1,
}
SubsumptionChecker SC(*this);
- std::optional<bool> Subsumes = SC.Subsumes(D1, AC1, D2, AC2);
+ std::optional<bool> Subsumes =
+ SC.Subsumes(D1, AC1, D2, AC2, /*DepthAdjusted=*/Depth1 != Depth2);
if (!Subsumes) {
// Normalization failed
return true;
@@ -2778,14 +2779,16 @@ void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) {
std::optional<bool> SubsumptionChecker::Subsumes(
const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ,
- ArrayRef<AssociatedConstraint> Q) {
+ ArrayRef<AssociatedConstraint> Q, bool DepthAdjusted) {
const NormalizedConstraint *PNormalized =
- SemaRef.getNormalizedAssociatedConstraints(DP, P);
+ SemaRef.getNormalizedAssociatedConstraints(DepthAdjusted ? nullptr : DP,
+ P);
if (!PNormalized)
return std::nullopt;
const NormalizedConstraint *QNormalized =
- SemaRef.getNormalizedAssociatedConstraints(DQ, Q);
+ SemaRef.getNormalizedAssociatedConstraints(DepthAdjusted ? nullptr : DQ,
+ Q);
if (!QNormalized)
return std::nullopt;
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 1d2ada3e0a398..628499bebea83 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1659,6 +1659,25 @@ void foo() { call(""); }
}
+namespace GH186624 {
+
+template <class T>
+concept C = __is_unsigned(T);
+
+template <C T>
+struct encoder_interface {};
+
+template <template <C> class CodecInterface, C T>
+CodecInterface<T>* create_codec() {
+ return nullptr;
+}
+
+encoder_interface<unsigned>* create_encoder() {
+ return create_codec<encoder_interface, unsigned>();
+}
+
+}
+
namespace GH170856 {
template <unsigned N, unsigned M> struct symbol_text {
>From 5167001b24324dd0ff135683966224c8b11b6be6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 16 Mar 2026 19:35:49 +0800
Subject: [PATCH 2/2] Do not break ABI
---
clang/include/clang/Sema/SemaConcept.h | 3 +--
clang/lib/Sema/SemaConcept.cpp | 16 +++++++++-------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index e6644f1bd1bee..bdd997b18cb08 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -431,8 +431,7 @@ class SubsumptionChecker {
std::optional<bool> Subsumes(const NamedDecl *DP,
ArrayRef<AssociatedConstraint> P,
const NamedDecl *DQ,
- ArrayRef<AssociatedConstraint> Q,
- bool DepthAdjusted);
+ ArrayRef<AssociatedConstraint> Q);
bool Subsumes(const NormalizedConstraint *P, const NormalizedConstraint *Q);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 16cbec22c823b..601b2deee2feb 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2544,8 +2544,12 @@ bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1,
}
SubsumptionChecker SC(*this);
- std::optional<bool> Subsumes =
- SC.Subsumes(D1, AC1, D2, AC2, /*DepthAdjusted=*/Depth1 != Depth2);
+ // Associated declarations are used as a cache key in the event they were
+ // normalized earlier during concept checking. However we cannot reuse these
+ // cached results if any of the template depths have been adjusted.
+ bool DepthAdjusted = Depth1 != Depth2;
+ std::optional<bool> Subsumes = SC.Subsumes(DepthAdjusted ? nullptr : D1, AC1,
+ DepthAdjusted ? nullptr : D2, AC2);
if (!Subsumes) {
// Normalization failed
return true;
@@ -2779,16 +2783,14 @@ void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) {
std::optional<bool> SubsumptionChecker::Subsumes(
const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ,
- ArrayRef<AssociatedConstraint> Q, bool DepthAdjusted) {
+ ArrayRef<AssociatedConstraint> Q) {
const NormalizedConstraint *PNormalized =
- SemaRef.getNormalizedAssociatedConstraints(DepthAdjusted ? nullptr : DP,
- P);
+ SemaRef.getNormalizedAssociatedConstraints(DP, P);
if (!PNormalized)
return std::nullopt;
const NormalizedConstraint *QNormalized =
- SemaRef.getNormalizedAssociatedConstraints(DepthAdjusted ? nullptr : DQ,
- Q);
+ SemaRef.getNormalizedAssociatedConstraints(DQ, Q);
if (!QNormalized)
return std::nullopt;
More information about the cfe-commits
mailing list