[clang] [Clang] Fix ICE in constraint normalization when substituting concept template parameters (PR #184406)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 4 09:22:01 PST 2026
https://github.com/wx257osn2 updated https://github.com/llvm/llvm-project/pull/184406
>From 2a6f2f9184e45cbedb0758d053c9bc573d1478aa Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Tue, 3 Mar 2026 21:39:30 +0900
Subject: [PATCH 01/11] add test
---
.../SemaCXX/cxx2c-template-template-param.cpp | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index 704df3112277f..6ba18a253d34c 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -432,5 +432,21 @@ template <typename T, template <typename...> concept C1>
concept TestBinary = T::a || C1<T>;
static_assert(TestBinary<int, A>);
+}
+
+namespace concept_template_parameter_as_second_parameter {
+
+template <typename>
+concept Any = true;
+
+template <typename T, template <typename...> concept C>
+concept Wrap = C<T>;
+
+template <typename T, Wrap<Any> U>
+void f(T&&, U&&) {}
+
+void test() {
+ f(0, 1);
+}
}
>From 554457662fdc3a24b2d929ff82294fc376145c17 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Tue, 3 Mar 2026 22:33:43 +0900
Subject: [PATCH 02/11] early return
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index b4d8158525f04..32b406b551247 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4481,11 +4481,10 @@ ExprResult Sema::SubstConceptTemplateArguments(
ExprResult TransformUnresolvedLookupExpr(UnresolvedLookupExpr *E,
bool IsAddressOfOperand = false) {
- if (E->isConceptReference()) {
- ExprResult Res = SemaRef.SubstExpr(E, MLTAL);
- return Res;
- }
- return E;
+ if (!E->isConceptReference())
+ return E;
+
+ return SemaRef.SubstExpr(E, MLTAL);
}
};
>From a4be99ae34ed291563005892a6466e6859488c56 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Wed, 4 Mar 2026 00:17:20 +0900
Subject: [PATCH 03/11] resolve concept with correct template id
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 31 +++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 32b406b551247..5015c6d4a460a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4484,7 +4484,36 @@ ExprResult Sema::SubstConceptTemplateArguments(
if (!E->isConceptReference())
return E;
- return SemaRef.SubstExpr(E, MLTAL);
+ NamedDecl *D = *E->decls_begin();
+ ConceptDecl *ResolvedConcept = nullptr;
+
+ if (const auto * const TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
+ const auto Depth = TTP->getDepth();
+ const auto Pos = TTP->getPosition();
+ if (Depth < MLTAL.getNumLevels() &&
+ MLTAL.hasTemplateArgument(Depth, Pos)) {
+ TemplateArgument Arg = MLTAL(Depth, Pos);
+ if (Arg.getKind() == TemplateArgument::Template)
+ ResolvedConcept = dyn_cast_or_null<ConceptDecl>(
+ Arg.getAsTemplate().getAsTemplateDecl());
+ }
+ } else
+ ResolvedConcept = dyn_cast<ConceptDecl>(D);
+
+ if (ResolvedConcept == nullptr)
+ return E;
+
+ TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
+ if (TransformTemplateArguments(E->getTemplateArgs(),
+ E->getNumTemplateArgs(), TransArgs))
+ return ExprError();
+
+ CXXScopeSpec SS;
+ DeclarationNameInfo NameInfo(ResolvedConcept->getDeclName(),
+ E->getNameLoc());
+ return SemaRef.CheckConceptTemplateId(SS, SourceLocation(), NameInfo,
+ ResolvedConcept, ResolvedConcept,
+ &TransArgs, false);
}
};
>From ec1a8d3767d8cab315b46b6b6695345a44676506 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 00:58:46 +0900
Subject: [PATCH 04/11] remove const
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 5015c6d4a460a..147cbe2974463 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4487,7 +4487,7 @@ ExprResult Sema::SubstConceptTemplateArguments(
NamedDecl *D = *E->decls_begin();
ConceptDecl *ResolvedConcept = nullptr;
- if (const auto * const TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
const auto Depth = TTP->getDepth();
const auto Pos = TTP->getPosition();
if (Depth < MLTAL.getNumLevels() &&
>From 80540cf4906e0c1e24c6c32609346916302ea90b Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 00:59:07 +0900
Subject: [PATCH 05/11] s/const auto/unsigned/
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 147cbe2974463..e94718c20e1da 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4488,8 +4488,8 @@ ExprResult Sema::SubstConceptTemplateArguments(
ConceptDecl *ResolvedConcept = nullptr;
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
- const auto Depth = TTP->getDepth();
- const auto Pos = TTP->getPosition();
+ unsigned Depth = TTP->getDepth();
+ unsigned Pos = TTP->getPosition();
if (Depth < MLTAL.getNumLevels() &&
MLTAL.hasTemplateArgument(Depth, Pos)) {
TemplateArgument Arg = MLTAL(Depth, Pos);
>From d0bdc4734568e84b5015ce213e14803088a778a3 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 01:19:39 +0900
Subject: [PATCH 06/11] use assert instead of if
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index e94718c20e1da..feb8684c680d4 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -40,6 +40,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/TimeProfiler.h"
+#include <iterator>
#include <optional>
using namespace clang;
@@ -4484,19 +4485,20 @@ ExprResult Sema::SubstConceptTemplateArguments(
if (!E->isConceptReference())
return E;
+ assert(std::next(E->decls_begin()) == E->decls_end() &&
+ "ConceptReference must have single declaration");
NamedDecl *D = *E->decls_begin();
ConceptDecl *ResolvedConcept = nullptr;
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
unsigned Depth = TTP->getDepth();
unsigned Pos = TTP->getPosition();
- if (Depth < MLTAL.getNumLevels() &&
- MLTAL.hasTemplateArgument(Depth, Pos)) {
- TemplateArgument Arg = MLTAL(Depth, Pos);
- if (Arg.getKind() == TemplateArgument::Template)
- ResolvedConcept = dyn_cast_or_null<ConceptDecl>(
- Arg.getAsTemplate().getAsTemplateDecl());
- }
+ assert(Depth < MLTAL.getNumLevels() &&
+ MLTAL.hasTemplateArgument(Depth, Pos));
+ TemplateArgument Arg = MLTAL(Depth, Pos);
+ assert(Arg.getKind() == TemplateArgument::Template);
+ ResolvedConcept = dyn_cast_or_null<ConceptDecl>(
+ Arg.getAsTemplate().getAsTemplateDecl());
} else
ResolvedConcept = dyn_cast<ConceptDecl>(D);
>From 3d3c6507ea296be77a83d31e3d0e5c5aa967e606 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 01:23:38 +0900
Subject: [PATCH 07/11] move early return into inner branch
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index feb8684c680d4..ada0ed390419c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4499,11 +4499,11 @@ ExprResult Sema::SubstConceptTemplateArguments(
assert(Arg.getKind() == TemplateArgument::Template);
ResolvedConcept = dyn_cast_or_null<ConceptDecl>(
Arg.getAsTemplate().getAsTemplateDecl());
+ if (ResolvedConcept == nullptr)
+ return E;
} else
ResolvedConcept = dyn_cast<ConceptDecl>(D);
- if (ResolvedConcept == nullptr)
- return E;
TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
if (TransformTemplateArguments(E->getTemplateArgs(),
>From d9fabd87e58f09d1fcabbd63f6e705100c6a1205 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 01:50:35 +0900
Subject: [PATCH 08/11] add test
---
.../SemaCXX/cxx2c-template-template-param.cpp | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index 6ba18a253d34c..6214e96905409 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -450,3 +450,23 @@ void test() {
}
}
+
+namespace multi_level_concept_template_parameter {
+
+template <typename>
+concept Any = true;
+
+template <typename T, template <typename...> concept C0, template <typename...> concept C1>
+concept Both = C0<T> && C1<T>;
+
+template <typename T, template <typename...> concept C>
+concept Wrap = Both<T, Any, C>;
+
+template <typename T, Wrap<Any> U>
+void f(T&&, U&&) {}
+
+void test() {
+ f(0, 1);
+}
+
+}
>From 67bd89112bb5c3e265f518ef79e39b98c29d6e08 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 01:51:28 +0900
Subject: [PATCH 09/11] getAsTemplateDecl() should be non-null for concepts
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ada0ed390419c..c05a801baaa49 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4497,7 +4497,7 @@ ExprResult Sema::SubstConceptTemplateArguments(
MLTAL.hasTemplateArgument(Depth, Pos));
TemplateArgument Arg = MLTAL(Depth, Pos);
assert(Arg.getKind() == TemplateArgument::Template);
- ResolvedConcept = dyn_cast_or_null<ConceptDecl>(
+ ResolvedConcept = dyn_cast<ConceptDecl>(
Arg.getAsTemplate().getAsTemplateDecl());
if (ResolvedConcept == nullptr)
return E;
>From 63da972275fd047fbd311145bd80aa0690ea8901 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 02:18:55 +0900
Subject: [PATCH 10/11] check number of decls directly
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index c05a801baaa49..adca9e1598be7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4485,7 +4485,7 @@ ExprResult Sema::SubstConceptTemplateArguments(
if (!E->isConceptReference())
return E;
- assert(std::next(E->decls_begin()) == E->decls_end() &&
+ assert(E->getNumDecls() == 1 &&
"ConceptReference must have single declaration");
NamedDecl *D = *E->decls_begin();
ConceptDecl *ResolvedConcept = nullptr;
>From 6ee8afedabed559a5dab86ead640acf01ebdf6ea Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 5 Mar 2026 02:21:34 +0900
Subject: [PATCH 11/11] revert to use assert
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index adca9e1598be7..557b2fe660247 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4493,12 +4493,13 @@ ExprResult Sema::SubstConceptTemplateArguments(
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
unsigned Depth = TTP->getDepth();
unsigned Pos = TTP->getPosition();
- assert(Depth < MLTAL.getNumLevels() &&
- MLTAL.hasTemplateArgument(Depth, Pos));
- TemplateArgument Arg = MLTAL(Depth, Pos);
- assert(Arg.getKind() == TemplateArgument::Template);
- ResolvedConcept = dyn_cast<ConceptDecl>(
- Arg.getAsTemplate().getAsTemplateDecl());
+ if (Depth < MLTAL.getNumLevels() &&
+ MLTAL.hasTemplateArgument(Depth, Pos)) {
+ TemplateArgument Arg = MLTAL(Depth, Pos);
+ assert(Arg.getKind() == TemplateArgument::Template);
+ ResolvedConcept = dyn_cast<ConceptDecl>(
+ Arg.getAsTemplate().getAsTemplateDecl());
+ }
if (ResolvedConcept == nullptr)
return E;
} else
More information about the cfe-commits
mailing list