[clang] [Clang] Substitute non dependent concepts in constraints (PR #163827)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 17 00:33:25 PDT 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/163827
>From 683aef0b6d71cdbc1ba269ddac55b29e27a595aa Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 16 Oct 2025 18:52:55 +0200
Subject: [PATCH 1/5] [Clang] Substitute non dependent concepts in constraints
This is
```
to form CE, any non-dependent concept template argument Ai
is substituted into the constraint-expression of C.
If any such substitution results in an invalid concept-id,
the program is ill-formed; no diagnostic is required.
```
And continues the implementation of P2841R7 (C++26).
No changelog, we will add an entry for P2841R7 closer to the
next release, depending on the state of avancement.
---
clang/include/clang/Sema/Sema.h | 7 ++
clang/lib/AST/TemplateBase.cpp | 15 +--
clang/lib/Sema/SemaConcept.cpp | 46 +++++++-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 103 ++++++++++++++++++
clang/lib/Sema/TreeTransform.h | 46 ++++++++
.../SemaCXX/cxx2c-template-template-param.cpp | 84 ++++++++++++++
6 files changed, 293 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 37598f8530c09..e2444bc03327f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13385,6 +13385,13 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Outputs);
+ /// Substitute concept template arguments in the constraint expression
+ /// of a concept-id. This is used to implement [temp.constr.normal].
+ ExprResult
+ SubstConceptTemplateArguments(const ConceptSpecializationExpr *CSE,
+ const Expr *ConstraintExpr,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
bool SubstTemplateArgumentsInParameterMapping(
ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 76f96fb8c5dcc..131ae6e8a478f 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -340,13 +340,14 @@ bool TemplateArgument::isPackExpansion() const {
}
bool TemplateArgument::isConceptOrConceptTemplateParameter() const {
- if (getKind() == TemplateArgument::Template) {
- if (isa<ConceptDecl>(getAsTemplate().getAsTemplateDecl()))
- return true;
- else if (auto *TTP = dyn_cast_if_present<TemplateTemplateParmDecl>(
- getAsTemplate().getAsTemplateDecl()))
- return TTP->templateParameterKind() == TNK_Concept_template;
- }
+ if (getKind() != TemplateArgument::Template)
+ return false;
+
+ if (isa_and_nonnull<ConceptDecl>(getAsTemplate().getAsTemplateDecl()))
+ return true;
+ if (auto *TTP = llvm::dyn_cast_or_null<TemplateTemplateParmDecl>(
+ getAsTemplate().getAsTemplateDecl()))
+ return TTP->templateParameterKind() == TNK_Concept_template;
return false;
}
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 87dd68269d44a..d352e5974f772 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1217,10 +1217,48 @@ bool Sema::CheckConstraintSatisfaction(
return false;
}
+static const ExprResult
+SubstituteConceptsInConstrainExpression(Sema &S, const NamedDecl *D,
+ const ConceptSpecializationExpr *CSE,
+ UnsignedOrNone SubstIndex) {
+
+ // [C++2c] [temp.constr.normal]
+ // Otherwise, to form CE, any non-dependent concept template argument Ai
+ // is substituted into the constraint-expression of C.
+ // If any such substitution results in an invalid concept-id,
+ // the program is ill-formed; no diagnostic is required.
+
+ ConceptDecl *Concept = CSE->getNamedConcept()->getCanonicalDecl();
+ Sema::ArgPackSubstIndexRAII _(S, SubstIndex);
+
+ const auto *ArgsAsWritten = CSE->getTemplateArgsAsWritten();
+ if (llvm::none_of(
+ ArgsAsWritten->arguments(), [&](const TemplateArgumentLoc &ArgLoc) {
+ return !ArgLoc.getArgument().isDependent() &&
+ ArgLoc.getArgument().isConceptOrConceptTemplateParameter();
+ })) {
+ return Concept->getConstraintExpr();
+ }
+
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ Concept, Concept->getLexicalDeclContext(),
+ /*Final=*/false, CSE->getTemplateArguments(),
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+ return S.SubstConceptTemplateArguments(CSE, Concept->getConstraintExpr(),
+ MLTAL);
+}
+
bool Sema::CheckConstraintSatisfaction(
const ConceptSpecializationExpr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
+ ExprResult Res = SubstituteConceptsInConstrainExpression(
+ *this, nullptr, ConstraintExpr, ArgPackSubstIndex);
+ if (!Res.isUsable())
+ return true;
+
llvm::SmallVector<AssociatedConstraint, 1> Constraints;
Constraints.emplace_back(
ConstraintExpr->getNamedConcept()->getConstraintExpr());
@@ -2249,8 +2287,14 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
// Use canonical declarations to merge ConceptDecls across
// different modules.
ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl();
+
+ ExprResult Res =
+ SubstituteConceptsInConstrainExpression(S, D, CSE, SubstIndex);
+ if (!Res.isUsable())
+ return nullptr;
+
SubNF = NormalizedConstraint::fromAssociatedConstraints(
- S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex));
+ S, CD, AssociatedConstraint(Res.get(), SubstIndex));
if (!SubNF)
return nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index bec282011b3fa..29e6b276d43c8 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4487,6 +4487,109 @@ ExprResult Sema::SubstConstraintExprWithoutSatisfaction(
return Instantiator.TransformExpr(E);
}
+ExprResult Sema::SubstConceptTemplateArguments(
+ const ConceptSpecializationExpr *CSE, const Expr *ConstraintExpr,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+ TemplateInstantiator Instantiator(*this, MLTAL, SourceLocation(),
+ DeclarationName());
+ auto *ArgsAsWritten = CSE->getTemplateArgsAsWritten();
+ TemplateArgumentListInfo SubstArgs(ArgsAsWritten->getLAngleLoc(),
+ ArgsAsWritten->getRAngleLoc());
+
+ Sema::InstantiatingTemplate Inst(
+ *this, ArgsAsWritten->arguments().front().getSourceRange().getBegin(),
+ Sema::InstantiatingTemplate::ConstraintNormalization{},
+ CSE->getNamedConcept(),
+ ArgsAsWritten->arguments().front().getSourceRange());
+
+ if (Instantiator.TransformConceptTemplateArguments(
+ ArgsAsWritten->getTemplateArgs(),
+ ArgsAsWritten->getTemplateArgs() +
+ ArgsAsWritten->getNumTemplateArgs(),
+ SubstArgs))
+ return true;
+
+ llvm::SmallVector<TemplateArgument, 4> NewArgList;
+ NewArgList.reserve(SubstArgs.arguments().size());
+ for (const auto &ArgLoc : SubstArgs.arguments())
+ NewArgList.push_back(ArgLoc.getArgument());
+
+ MultiLevelTemplateArgumentList MLTALForConstraint =
+ getTemplateInstantiationArgs(
+ CSE->getNamedConcept(),
+ CSE->getNamedConcept()->getLexicalDeclContext(),
+ /*Final=*/false,
+ /*Innermost=*/NewArgList,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+
+ struct ConstraintExprTransformer : TreeTransform<ConstraintExprTransformer> {
+ using Base = TreeTransform<ConstraintExprTransformer>;
+ MultiLevelTemplateArgumentList &MLTAL;
+
+ ConstraintExprTransformer(Sema &SemaRef,
+ MultiLevelTemplateArgumentList &MLTAL)
+ : TreeTransform(SemaRef), MLTAL(MLTAL) {}
+
+ ExprResult TransformExpr(Expr *E) {
+ if (!E)
+ return E;
+ switch (E->getStmtClass()) {
+ case Stmt::BinaryOperatorClass:
+ case Stmt::ConceptSpecializationExprClass:
+ case Stmt::ParenExprClass:
+ case Stmt::UnresolvedLookupExprClass:
+ return Base::TransformExpr(E);
+ default:
+ break;
+ }
+ return E;
+ }
+
+ ExprResult TransformBinaryOperator(BinaryOperator *E) {
+ if (!(E->getOpcode() == BinaryOperatorKind::BO_LAnd ||
+ E->getOpcode() == BinaryOperatorKind::BO_LOr))
+ return E;
+
+ ExprResult LHS = TransformExpr(E->getLHS());
+ ExprResult RHS = TransformExpr(E->getRHS());
+
+ if (LHS.get() == E->getLHS() && RHS.get() == E->getRHS())
+ return E;
+
+ return BinaryOperator::Create(SemaRef.Context, LHS.get(), RHS.get(),
+ E->getOpcode(), SemaRef.Context.BoolTy,
+ VK_PRValue, OK_Ordinary,
+ E->getOperatorLoc(), FPOptionsOverride{});
+ }
+
+ bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
+ TemplateArgumentLoc &Output,
+ bool Uneval = false) {
+ if (Input.getArgument().isConceptOrConceptTemplateParameter())
+ return Base::TransformTemplateArgument(Input, Output, Uneval);
+
+ Output = Input;
+ return false;
+ }
+
+ ExprResult TransformUnresolvedLookupExpr(UnresolvedLookupExpr *E,
+ bool IsAddressOfOperand = false) {
+ if (E->isConceptReference()) {
+ ExprResult Res = SemaRef.SubstExpr(E, MLTAL);
+ return Res;
+ }
+ return E;
+ }
+ };
+
+ ConstraintExprTransformer Transformer(*this, MLTALForConstraint);
+ ExprResult Res =
+ Transformer.TransformExpr(const_cast<Expr *>(ConstraintExpr));
+ return Res;
+}
+
ExprResult Sema::SubstInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 86896abc1f775..97c6f7066cff9 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -694,6 +694,12 @@ class TreeTransform {
TemplateArgumentListInfo &Outputs,
bool Uneval = false);
+ template <typename InputIterator>
+ bool TransformConceptTemplateArguments(InputIterator First,
+ InputIterator Last,
+ TemplateArgumentListInfo &Outputs,
+ bool Uneval = false);
+
/// Checks if the argument pack from \p In will need to be expanded and does
/// the necessary prework.
/// Whether the expansion is needed is captured in Info.Expand.
@@ -5192,6 +5198,46 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
return false;
}
+template <typename Derived>
+template <typename InputIterator>
+bool TreeTransform<Derived>::TransformConceptTemplateArguments(
+ InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs,
+ bool Uneval) {
+
+ auto isNonDependentConcept = [](const TemplateArgument &Arg) {
+ return !Arg.isDependent() && Arg.isConceptOrConceptTemplateParameter();
+ };
+
+ for (; First != Last; ++First) {
+ TemplateArgumentLoc Out;
+ TemplateArgumentLoc In = *First;
+
+ if (In.getArgument().getKind() == TemplateArgument::Pack) {
+ typedef TemplateArgumentLocInventIterator<Derived,
+ TemplateArgument::pack_iterator>
+ PackLocIterator;
+ if (TransformConceptTemplateArguments(
+ PackLocIterator(*this, In.getArgument().pack_begin()),
+ PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
+ Uneval))
+ return true;
+ continue;
+ }
+
+ if (!isNonDependentConcept(In.getArgument())) {
+ Outputs.addArgument(In);
+ continue;
+ }
+
+ if (getDerived().TransformTemplateArgument(In, Out, Uneval))
+ return true;
+
+ Outputs.addArgument(Out);
+ }
+
+ return false;
+}
+
// FIXME: Find ways to reduce code duplication for pack expansions.
template <typename Derived>
bool TreeTransform<Derived>::PreparePackForExpansion(TemplateArgumentLoc In,
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index 4ad3fd95039cd..77f872d31d923 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -350,3 +350,87 @@ template <A<concept missing<int>> T> // expected-error {{expected expression}} \
// expected-error {{expected unqualified-id}}
auto f();
}
+
+namespace concept_arg_normalization {
+
+template <typename T,
+ template <typename...> concept C1>
+concept one = (C1<T>); // #concept-arg-one
+
+template <typename T>
+concept A = true; // #concept-arg-A
+
+template <typename T>
+concept BetterA = A<T> && true;
+
+template <typename T>
+concept B = true; // #concept-arg-B
+
+template <typename T>
+concept False = false; // #concept-arg-False
+
+template <typename T>
+requires one<T, A>
+void f1(T){} // #concept-arg-f1-1
+
+template <typename T>
+requires one<T, B>
+void f1(T){} // #concept-arg-f1-2
+
+template <typename T>
+requires one<T, A>
+void f2(T){}
+
+template <typename T>
+requires one<T, BetterA>
+void f2(T){}
+
+
+template <template <typename> concept CT>
+requires one<int, A>
+void f3(){} // #concept-arg-f3-1
+
+template <template <typename> concept CT>
+requires one<int, CT>
+void f3(){} // #concept-arg-f3-2
+
+template <typename T>
+requires one<T, False> void f4(T){} // #concept-arg-f4
+
+
+void test() {
+ f1(0);
+ // expected-error at -1 {{call to 'f1' is ambiguous}}
+ // expected-note@#concept-arg-f1-1{{candidate function [with T = int]}}
+ // expected-note@#concept-arg-f1-2{{candidate function [with T = int]}}
+ // expected-note@#concept-arg-A {{similar constraint expressions not considered equivalent}}
+ // expected-note@#concept-arg-B {{similar constraint expression here}}
+ f2(0);
+
+ f3<BetterA>();
+ // expected-error at -1 {{call to 'f3' is ambiguous}}
+ // expected-note@#concept-arg-f3-1 {{candidate function [with CT = concept_arg_normalization::BetterA]}}
+ // expected-note@#concept-arg-f3-2 {{candidate function [with CT = concept_arg_normalization::BetterA]}}
+
+static_assert(one<int, A>);
+static_assert(one<int, False>);
+// expected-error at -1 {{static assertion failed}} \
+// expected-note at -1 {{because 'one<int, False>' evaluated to false}}
+// expected-note@#concept-arg-one {{because 'int' does not satisfy 'False'}}
+// expected-note@#concept-arg-False {{because 'false' evaluated to false}}
+
+f4(0);
+// expected-error at -1 {{no matching function for call to 'f4'}}
+// expected-note@#concept-arg-f4 {{candidate template ignored: constraints not satisfied [with T = int]}}
+// expected-note@#concept-arg-f4 {{because 'one<int, False>'}}
+// expected-note@#concept-arg-one {{because 'int' does not satisfy 'False'}}
+// expected-note@#concept-arg-False {{because 'false' evaluated to false}}
+
+
+template <typename T, template <typename...> concept C1>
+concept TestBinary = T::a || C1<T>;
+static_assert(TestBinary<int, A>);
+
+}
+
+}
>From 4c943e60728798d7285e82b074aafc5c6618224b Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 16 Oct 2025 19:27:08 +0200
Subject: [PATCH 2/5] fix test
---
clang/test/SemaCXX/cxx2c-template-template-param.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index 77f872d31d923..704df3112277f 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -426,11 +426,11 @@ f4(0);
// expected-note@#concept-arg-one {{because 'int' does not satisfy 'False'}}
// expected-note@#concept-arg-False {{because 'false' evaluated to false}}
+}
template <typename T, template <typename...> concept C1>
concept TestBinary = T::a || C1<T>;
static_assert(TestBinary<int, A>);
-}
}
>From 05fc8d8678a2c16d39aefc893a927ce0fdd7aec7 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 17 Oct 2025 09:15:20 +0200
Subject: [PATCH 3/5] address feedback
---
clang/lib/Sema/SemaConcept.cpp | 6 +++---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 13 ++++++++-----
clang/lib/Sema/TreeTransform.h | 7 +++++--
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index d352e5974f772..04a73181831d8 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1231,7 +1231,8 @@ SubstituteConceptsInConstrainExpression(Sema &S, const NamedDecl *D,
ConceptDecl *Concept = CSE->getNamedConcept()->getCanonicalDecl();
Sema::ArgPackSubstIndexRAII _(S, SubstIndex);
- const auto *ArgsAsWritten = CSE->getTemplateArgsAsWritten();
+ const ASTTemplateArgumentListInfo *ArgsAsWritten =
+ CSE->getTemplateArgsAsWritten();
if (llvm::none_of(
ArgsAsWritten->arguments(), [&](const TemplateArgumentLoc &ArgLoc) {
return !ArgLoc.getArgument().isDependent() &&
@@ -1260,8 +1261,7 @@ bool Sema::CheckConstraintSatisfaction(
return true;
llvm::SmallVector<AssociatedConstraint, 1> Constraints;
- Constraints.emplace_back(
- ConstraintExpr->getNamedConcept()->getConstraintExpr());
+ Constraints.emplace_back(Res.get());
MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(),
ConstraintExpr->getTemplateArguments(),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 29e6b276d43c8..6ed401dc4a8c1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4492,7 +4492,8 @@ ExprResult Sema::SubstConceptTemplateArguments(
const MultiLevelTemplateArgumentList &MLTAL) {
TemplateInstantiator Instantiator(*this, MLTAL, SourceLocation(),
DeclarationName());
- auto *ArgsAsWritten = CSE->getTemplateArgsAsWritten();
+ const ASTTemplateArgumentListInfo *ArgsAsWritten =
+ CSE->getTemplateArgsAsWritten();
TemplateArgumentListInfo SubstArgs(ArgsAsWritten->getLAngleLoc(),
ArgsAsWritten->getRAngleLoc());
@@ -4502,6 +4503,9 @@ ExprResult Sema::SubstConceptTemplateArguments(
CSE->getNamedConcept(),
ArgsAsWritten->arguments().front().getSourceRange());
+ if (Inst.isInvalid())
+ return ExprError();
+
if (Instantiator.TransformConceptTemplateArguments(
ArgsAsWritten->getTemplateArgs(),
ArgsAsWritten->getTemplateArgs() +
@@ -4509,10 +4513,9 @@ ExprResult Sema::SubstConceptTemplateArguments(
SubstArgs))
return true;
- llvm::SmallVector<TemplateArgument, 4> NewArgList;
- NewArgList.reserve(SubstArgs.arguments().size());
- for (const auto &ArgLoc : SubstArgs.arguments())
- NewArgList.push_back(ArgLoc.getArgument());
+ llvm::SmallVector<TemplateArgument, 4> NewArgList = llvm::map_to_vector(
+ SubstArgs.arguments(),
+ [](const TemplateArgumentLoc &Loc) { return Loc.getArgument(); });
MultiLevelTemplateArgumentList MLTALForConstraint =
getTemplateInstantiationArgs(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 97c6f7066cff9..29f0c30c6534e 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5204,7 +5204,10 @@ bool TreeTransform<Derived>::TransformConceptTemplateArguments(
InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs,
bool Uneval) {
- auto isNonDependentConcept = [](const TemplateArgument &Arg) {
+ // [C++26][temp.constr.normal]
+ // any non-dependent concept template argument
+ // is substituted into the constraint-expression of C.
+ auto isNonDependentConceptArgument = [](const TemplateArgument &Arg) {
return !Arg.isDependent() && Arg.isConceptOrConceptTemplateParameter();
};
@@ -5224,7 +5227,7 @@ bool TreeTransform<Derived>::TransformConceptTemplateArguments(
continue;
}
- if (!isNonDependentConcept(In.getArgument())) {
+ if (!isNonDependentConceptArgument(In.getArgument())) {
Outputs.addArgument(In);
continue;
}
>From aa915a72f09477e0ea6da2d07e337eb46c2a3b0c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 17 Oct 2025 09:20:56 +0200
Subject: [PATCH 4/5] add comments
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6ed401dc4a8c1..2648c70ed90d2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4527,6 +4527,10 @@ ExprResult Sema::SubstConceptTemplateArguments(
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
+ // Rebuild a constraint, only substituting non-dependent concept names
+ // and nothing else.
+ // Given C<SomeType, SomeValue, SomeConceptName, SomeDependentConceptName>.
+ // only SomeConceptName is substituted, in the constraint expression of C.
struct ConstraintExprTransformer : TreeTransform<ConstraintExprTransformer> {
using Base = TreeTransform<ConstraintExprTransformer>;
MultiLevelTemplateArgumentList &MLTAL;
@@ -4550,6 +4554,9 @@ ExprResult Sema::SubstConceptTemplateArguments(
return E;
}
+ // Rebuild both branches of a conjunction / disjunction
+ // even if there is a substitution failure in one of
+ // the branch.
ExprResult TransformBinaryOperator(BinaryOperator *E) {
if (!(E->getOpcode() == BinaryOperatorKind::BO_LAnd ||
E->getOpcode() == BinaryOperatorKind::BO_LOr))
>From b247262ccb2005c0c8411c52454a2cd6b2419bee Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 17 Oct 2025 09:32:21 +0200
Subject: [PATCH 5/5] miss8ing header
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 2648c70ed90d2..ca7e3b264cec4 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -35,6 +35,7 @@
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/TemplateInstCallback.h"
+#include "llvm/ADT/SmallVectorExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
More information about the cfe-commits
mailing list