summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/DeclTemplate.h6
-rw-r--r--clang/include/clang/Sema/Template.h18
-rw-r--r--clang/lib/Sema/SemaConcept.cpp51
-rw-r--r--clang/lib/Sema/SemaOverload.cpp2
-rw-r--r--clang/lib/Sema/SemaTemplateDeduction.cpp2
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp47
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp61
-rw-r--r--clang/test/SemaTemplate/concepts-friends.cpp24
-rw-r--r--clang/test/SemaTemplate/concepts-out-of-line-def.cpp272
-rw-r--r--clang/test/SemaTemplate/concepts.cpp98
10 files changed, 518 insertions, 63 deletions
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 3677335fa176..7cd505218f2b 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -2309,9 +2309,15 @@ protected:
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
}
+ void setCommonPtr(Common *C) {
+ RedeclarableTemplateDecl::Common = C;
+ }
+
public:
+
friend class ASTDeclReader;
friend class ASTDeclWriter;
+ friend class TemplateDeclInstantiator;
/// Load any lazily-loaded specializations from the external source.
void LoadLazySpecializations() const;
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 48e8b78311e1..1de2cc6917b4 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -232,9 +232,21 @@ enum class TemplateSubstitutionKind : char {
/// Replaces the current 'innermost' level with the provided argument list.
/// This is useful for type deduction cases where we need to get the entire
/// list from the AST, but then add the deduced innermost list.
- void replaceInnermostTemplateArguments(ArgList Args) {
- assert(TemplateArgumentLists.size() > 0 && "Replacing in an empty list?");
- TemplateArgumentLists[0].Args = Args;
+ void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
+ assert((!TemplateArgumentLists.empty() || NumRetainedOuterLevels) &&
+ "Replacing in an empty list?");
+
+ if (!TemplateArgumentLists.empty()) {
+ assert((TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ||
+ TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ==
+ AssociatedDecl) &&
+ "Trying to change incorrect declaration?");
+ TemplateArgumentLists[0].Args = Args;
+ } else {
+ --NumRetainedOuterLevels;
+ TemplateArgumentLists.push_back(
+ {{AssociatedDecl, /*Final=*/false}, Args});
+ }
}
/// Add an outermost level that we are not substituting. We have no
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 7aa06b615fec..1126c2c517fe 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -722,7 +722,7 @@ CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
ND, /*Final=*/false, /*Innermost=*/nullptr, /*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true, SkipForSpecialization);
- return MLTAL.getNumSubstitutedLevels();
+ return MLTAL.getNumLevels();
}
namespace {
@@ -753,27 +753,44 @@ namespace {
};
} // namespace
+static const Expr *SubstituteConstraintExpression(Sema &S, const NamedDecl *ND,
+ const Expr *ConstrExpr) {
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ ND, /*Final=*/false, /*Innermost=*/nullptr,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
+ /*SkipForSpecialization*/ false);
+ if (MLTAL.getNumSubstitutedLevels() == 0)
+ return ConstrExpr;
+
+ Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/false);
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ if (auto *RD = dyn_cast<CXXRecordDecl>(ND->getDeclContext()))
+ ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
+ ExprResult SubstConstr =
+ S.SubstConstraintExpr(const_cast<clang::Expr *>(ConstrExpr), MLTAL);
+ if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
+ return nullptr;
+ return SubstConstr.get();
+}
+
bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
const Expr *OldConstr,
const NamedDecl *New,
const Expr *NewConstr) {
+ if (OldConstr == NewConstr)
+ return true;
if (Old && New && Old != New) {
- unsigned Depth1 = CalculateTemplateDepthForConstraints(
- *this, Old);
- unsigned Depth2 = CalculateTemplateDepthForConstraints(
- *this, New);
-
- // Adjust the 'shallowest' verison of this to increase the depth to match
- // the 'other'.
- if (Depth2 > Depth1) {
- OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1)
- .TransformExpr(const_cast<Expr *>(OldConstr))
- .get();
- } else if (Depth1 > Depth2) {
- NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2)
- .TransformExpr(const_cast<Expr *>(NewConstr))
- .get();
- }
+ if (const Expr *SubstConstr =
+ SubstituteConstraintExpression(*this, Old, OldConstr))
+ OldConstr = SubstConstr;
+ else
+ return false;
+ if (const Expr *SubstConstr =
+ SubstituteConstraintExpression(*this, New, NewConstr))
+ NewConstr = SubstConstr;
+ else
+ return false;
}
llvm::FoldingSetNodeID ID1, ID2;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 29d6bd852ec6..135bf8053a96 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1297,7 +1297,7 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
// We check the return type and template parameter lists for function
// templates first; the remaining checks follow.
bool SameTemplateParameterList = TemplateParameterListsAreEqual(
- NewTemplate->getTemplateParameters(),
+ NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch);
bool SameReturnType = Context.hasSameType(Old->getDeclaredReturnType(),
New->getDeclaredReturnType());
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index b249d389bb67..db72b8b3089e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2882,7 +2882,7 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,
// not class-scope explicit specialization, so replace with Deduced Args
// instead of adding to inner-most.
if (NeedsReplacement)
- MLTAL.replaceInnermostTemplateArguments(CanonicalDeducedArgs);
+ MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index a25f8175ebcd..3d5da5f8d828 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -133,6 +133,14 @@ HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP,
return Response::Done();
}
+Response HandlePartialClassTemplateSpec(
+ const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec,
+ MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) {
+ if (!SkipForSpecialization)
+ Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth());
+ return Response::Done();
+}
+
// Add template arguments from a class template instantiation.
Response
HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
@@ -210,6 +218,21 @@ Response HandleFunction(const FunctionDecl *Function,
return Response::UseNextDecl(Function);
}
+Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
+ MultiLevelTemplateArgumentList &Result) {
+ if (!isa<ClassTemplateSpecializationDecl>(FTD->getDeclContext())) {
+ NestedNameSpecifier *NNS = FTD->getTemplatedDecl()->getQualifier();
+ const Type *Ty;
+ const TemplateSpecializationType *TSTy;
+ if (NNS && (Ty = NNS->getAsType()) &&
+ (TSTy = Ty->getAs<TemplateSpecializationType>()))
+ Result.addOuterTemplateArguments(const_cast<FunctionTemplateDecl *>(FTD),
+ TSTy->template_arguments(),
+ /*Final=*/false);
+ }
+ return Response::ChangeDecl(FTD->getLexicalDeclContext());
+}
+
Response HandleRecordDecl(const CXXRecordDecl *Rec,
MultiLevelTemplateArgumentList &Result,
ASTContext &Context,
@@ -220,15 +243,10 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec,
"Outer template not instantiated?");
if (ClassTemplate->isMemberSpecialization())
return Response::Done();
- if (ForConstraintInstantiation) {
- QualType RecordType = Context.getTypeDeclType(Rec);
- QualType Injected = cast<InjectedClassNameType>(RecordType)
- ->getInjectedSpecializationType();
- const auto *InjectedType = cast<TemplateSpecializationType>(Injected);
+ if (ForConstraintInstantiation)
Result.addOuterTemplateArguments(const_cast<CXXRecordDecl *>(Rec),
- InjectedType->template_arguments(),
+ ClassTemplate->getInjectedTemplateArgs(),
/*Final=*/false);
- }
}
bool IsFriend = Rec->getFriendObjectKind() ||
@@ -296,18 +314,23 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
// Accumulate the set of template argument lists in this structure.
MultiLevelTemplateArgumentList Result;
- if (Innermost)
+ using namespace TemplateInstArgsHelpers;
+ const Decl *CurDecl = ND;
+ if (Innermost) {
Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND),
Innermost->asArray(), Final);
-
- const Decl *CurDecl = ND;
+ CurDecl = Response::UseNextDecl(ND).NextDecl;
+ }
while (!CurDecl->isFileContextDecl()) {
- using namespace TemplateInstArgsHelpers;
Response R;
if (const auto *VarTemplSpec =
dyn_cast<VarTemplateSpecializationDecl>(CurDecl)) {
R = HandleVarTemplateSpec(VarTemplSpec, Result, SkipForSpecialization);
+ } else if (const auto *PartialClassTemplSpec =
+ dyn_cast<ClassTemplatePartialSpecializationDecl>(CurDecl)) {
+ R = HandlePartialClassTemplateSpec(PartialClassTemplSpec, Result,
+ SkipForSpecialization);
} else if (const auto *ClassTemplSpec =
dyn_cast<ClassTemplateSpecializationDecl>(CurDecl)) {
R = HandleClassTemplateSpec(ClassTemplSpec, Result,
@@ -320,6 +343,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
} else if (const auto *CSD =
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
+ } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CurDecl)) {
+ R = HandleFunctionTemplateDecl(FTD, Result);
} else if (!isa<DeclContext>(CurDecl)) {
R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
if (CurDecl->getDeclContext()->isTranslationUnit()) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e74b2b2e868e..3c78343eb861 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1654,33 +1654,12 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
<< QualifierLoc.getSourceRange();
return nullptr;
}
-
- if (PrevClassTemplate) {
- const ClassTemplateDecl *MostRecentPrevCT =
- PrevClassTemplate->getMostRecentDecl();
- TemplateParameterList *PrevParams =
- MostRecentPrevCT->getTemplateParameters();
-
- // Make sure the parameter lists match.
- if (!SemaRef.TemplateParameterListsAreEqual(
- D->getTemplatedDecl(), InstParams,
- MostRecentPrevCT->getTemplatedDecl(), PrevParams, true,
- Sema::TPL_TemplateMatch))
- return nullptr;
-
- // Do some additional validation, then merge default arguments
- // from the existing declarations.
- if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
- Sema::TPC_ClassTemplate))
- return nullptr;
- }
}
CXXRecordDecl *RecordInst = CXXRecordDecl::Create(
SemaRef.Context, Pattern->getTagKind(), DC, Pattern->getBeginLoc(),
Pattern->getLocation(), Pattern->getIdentifier(), PrevDecl,
/*DelayTypeCreation=*/true);
-
if (QualifierLoc)
RecordInst->setQualifierInfo(QualifierLoc);
@@ -1690,16 +1669,38 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
ClassTemplateDecl *Inst
= ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(),
D->getIdentifier(), InstParams, RecordInst);
- assert(!(isFriend && Owner->isDependentContext()));
- Inst->setPreviousDecl(PrevClassTemplate);
-
RecordInst->setDescribedClassTemplate(Inst);
if (isFriend) {
- if (PrevClassTemplate)
+ assert(!Owner->isDependentContext());
+ Inst->setLexicalDeclContext(Owner);
+ RecordInst->setLexicalDeclContext(Owner);
+
+ if (PrevClassTemplate) {
+ Inst->setCommonPtr(PrevClassTemplate->getCommonPtr());
+ RecordInst->setTypeForDecl(
+ PrevClassTemplate->getTemplatedDecl()->getTypeForDecl());
+ const ClassTemplateDecl *MostRecentPrevCT =
+ PrevClassTemplate->getMostRecentDecl();
+ TemplateParameterList *PrevParams =
+ MostRecentPrevCT->getTemplateParameters();
+
+ // Make sure the parameter lists match.
+ if (!SemaRef.TemplateParameterListsAreEqual(
+ RecordInst, InstParams, MostRecentPrevCT->getTemplatedDecl(),
+ PrevParams, true, Sema::TPL_TemplateMatch))
+ return nullptr;
+
+ // Do some additional validation, then merge default arguments
+ // from the existing declarations.
+ if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
+ Sema::TPC_ClassTemplate))
+ return nullptr;
+
Inst->setAccess(PrevClassTemplate->getAccess());
- else
+ } else {
Inst->setAccess(D->getAccess());
+ }
Inst->setObjectOfFriendDecl();
// TODO: do we want to track the instantiation progeny of this
@@ -1710,15 +1711,15 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
Inst->setInstantiatedFromMemberTemplate(D);
}
+ Inst->setPreviousDecl(PrevClassTemplate);
+
// Trigger creation of the type for the instantiation.
- SemaRef.Context.getInjectedClassNameType(RecordInst,
- Inst->getInjectedClassNameSpecialization());
+ SemaRef.Context.getInjectedClassNameType(
+ RecordInst, Inst->getInjectedClassNameSpecialization());
// Finish handling of friends.
if (isFriend) {
DC->makeDeclVisibleInContext(Inst);
- Inst->setLexicalDeclContext(Owner);
- RecordInst->setLexicalDeclContext(Owner);
return Inst;
}
diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
index 3a9b308a65c5..5c4609520a3c 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -441,3 +441,27 @@ namespace NTTP {
templ_func<1>(u2);
}
}
+
+
+namespace FriendOfFriend {
+
+template <typename>
+concept Concept = true;
+
+template <Concept> class FriendOfBar;
+
+template <Concept> class Bar {
+ template <Concept> friend class FriendOfBar;
+};
+
+Bar<void> BarInstance;
+
+namespace internal {
+void FriendOfFoo(FriendOfBar<void>);
+}
+
+template <Concept> class Foo {
+ friend void internal::FriendOfFoo(FriendOfBar<void>);
+};
+
+} // namespace FriendOfFriend
diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
index 222b78e0d22f..b7c91712f871 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -127,3 +127,275 @@ static_assert(S<XY>::specialization("str") == SPECIALIZATION_CONCEPT);
static_assert(S<int>::specialization("str") == SPECIALIZATION_REQUIRES);
} // namespace multiple_template_parameter_lists
+
+static constexpr int CONSTRAINED_METHOD_1 = 1;
+static constexpr int CONSTRAINED_METHOD_2 = 2;
+
+namespace constrained_members {
+
+template <int>
+struct S {
+ template <Concept C>
+ static constexpr int constrained_method();
+};
+
+template <>
+template <Concept C>
+constexpr int S<1>::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template <>
+template <Concept C>
+constexpr int S<2>::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S<1>::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S<2>::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+
+template <class T1, class T2>
+concept ConceptT1T2 = true;
+
+template<typename T3>
+struct S12 {
+ template<ConceptT1T2<T3> T4>
+ static constexpr int constrained_method();
+};
+
+template<>
+template<ConceptT1T2<int> T5>
+constexpr int S12<int>::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template<>
+template<ConceptT1T2<double> T5>
+constexpr int S12<double>::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S12<int>::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S12<double>::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+} // namespace constrained members
+
+namespace constrained_members_of_nested_types {
+
+template <int>
+struct S {
+ struct Inner0 {
+ struct Inner1 {
+ template <Concept C>
+ static constexpr int constrained_method();
+ };
+ };
+};
+
+template <>
+template <Concept C>
+constexpr int S<1>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template <>
+template <Concept C>
+constexpr int S<2>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S<1>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S<2>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+
+template <class T1, class T2>
+concept ConceptT1T2 = true;
+
+template<typename T3>
+struct S12 {
+ struct Inner0 {
+ struct Inner1 {
+ template<ConceptT1T2<T3> T4>
+ static constexpr int constrained_method();
+ };
+ };
+};
+
+template<>
+template<ConceptT1T2<int> T5>
+constexpr int S12<int>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template<>
+template<ConceptT1T2<double> T5>
+constexpr int S12<double>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S12<int>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S12<double>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+} // namespace constrained_members_of_nested_types
+
+namespace constrained_member_sfinae {
+
+template<int N> struct S {
+ template<class T>
+ static constexpr int constrained_method() requires (sizeof(int[N * 1073741824 + 4]) == 16) {
+ return CONSTRAINED_METHOD_1;
+ }
+
+ template<class T>
+ static constexpr int constrained_method() requires (sizeof(int[N]) == 16);
+};
+
+template<>
+template<typename T>
+constexpr int S<4>::constrained_method() requires (sizeof(int[4]) == 16) {
+ return CONSTRAINED_METHOD_2;
+}
+
+// Verify that there is no amiguity in this case.
+static_assert(S<4>::constrained_method<double>() == CONSTRAINED_METHOD_2);
+
+} // namespace constrained_member_sfinae
+
+namespace requires_expression_references_members {
+
+void accept1(int x);
+void accept2(XY xy);
+
+template <class T> struct S {
+ T Field = T();
+
+ constexpr int constrained_method()
+ requires requires { accept1(Field); };
+
+ constexpr int constrained_method()
+ requires requires { accept2(Field); };
+};
+
+template <class T>
+constexpr int S<T>::constrained_method()
+ requires requires { accept1(Field); } {
+ return CONSTRAINED_METHOD_1;
+}
+
+template <class T>
+constexpr int S<T>::constrained_method()
+ requires requires { accept2(Field); } {
+ return CONSTRAINED_METHOD_2;
+}
+
+static_assert(S<int>().constrained_method() == CONSTRAINED_METHOD_1);
+static_assert(S<XY>().constrained_method() == CONSTRAINED_METHOD_2);
+
+} // namespace requires_expression_references_members
+
+namespace GH60231 {
+
+template<typename T0> concept C = true;
+
+template <typename T1>
+struct S {
+ template <typename F1> requires C<S<T1>>
+ void foo1(F1 f);
+
+ template <typename F2>
+ void foo2(F2 f) requires C<S<T1>>;
+
+ template <typename F3> requires C<F3>
+ void foo3(F3 f);
+};
+
+template <typename T2>
+template <typename F4> requires C<S<T2>>
+void S<T2>::foo1(F4 f) {}
+
+template <typename T3>
+template <typename F5>
+void S<T3>::foo2(F5 f) requires C<S<T3>> {}
+
+template <typename T4>
+template <typename F6> requires C<F6>
+void S<T4>::foo3(F6 f) {}
+
+} // namespace GH60231
+
+namespace GH62003 {
+
+template <typename T0> concept Concept = true;
+
+template <class T1>
+struct S1 {
+ template <Concept C1>
+ static constexpr int foo();
+};
+template <class T2>
+template <Concept C2>
+constexpr int S1<T2>::foo() { return 1; }
+
+template <Concept C3>
+struct S2 {
+ template <class T3>
+ static constexpr int foo();
+};
+template <Concept C4>
+template <class T4>
+constexpr int S2<C4>::foo() { return 2; }
+
+template <Concept C5>
+struct S3 {
+ template <Concept C6>
+ static constexpr int foo();
+};
+template <Concept C7>
+template <Concept C8>
+constexpr int S3<C7>::foo() { return 3; }
+
+static_assert(S1<int>::foo<int>() == 1);
+static_assert(S2<int>::foo<int>() == 2);
+static_assert(S3<int>::foo<int>() == 3);
+
+} // namespace GH62003
+
+namespace MultilevelTemplateWithPartialSpecialization {
+template <typename>
+concept Concept = true;
+
+namespace two_level {
+template <typename T1, int>
+struct W0 {
+ template <typename T2>
+ requires (Concept<T2>)
+ void f(const T2 &);
+};
+
+template <typename T3>
+struct W0<T3, 0> {
+ template <typename T4>
+ requires (Concept<T4>)
+ void f(const T4 &);
+};
+
+template <typename T3>
+template <typename T4>
+requires (Concept<T4>)
+inline void W0<T3, 0>::f(const T4 &) {}
+} // namespace two_level
+
+namespace three_level {
+template <typename T1, int>
+struct W0 {
+ template <typename T2>
+ struct W1 {
+ template <typename T3>
+ requires (Concept<T3>)
+ void f(const T3 &);
+ };
+};
+
+template <typename T4>
+struct W0<T4, 0> {
+ template <typename T5>
+ struct W1 {
+ template <typename T6>
+ requires (Concept<T6>)
+ void f(const T6 &);
+ };
+};
+
+template <typename T7>
+template <typename T8>
+template <typename T9>
+requires (Concept<T9>)
+inline void W0<T7, 0>::W1<T8>::f(const T9 &) {}
+} // namespace three_level
+
+} // namespace MultilevelTemplateWithPartialSpecialization
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index d348e7a7efaf..c6fb5c441abd 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -816,3 +816,101 @@ static_assert(Parent<int>::TakesBinary<int, 0>::i == 0);
static_assert(Parent<int>::TakesBinary<int, 0ULL>::i == 0);
}
+namespace TemplateInsideNonTemplateClass {
+template<typename T, typename U> concept C = true;
+
+template<typename T> auto L = []<C<T> U>() {};
+
+struct Q {
+ template<C<int> U> friend constexpr auto decltype(L<int>)::operator()() const;
+};
+
+template <class T>
+concept C1 = false;
+
+struct Foo {
+ template <typename>
+ struct Bar {};
+
+ template <typename T>
+ requires(C1<T>)
+ struct Bar<T>;
+};
+
+Foo::Bar<int> BarInstance;
+} // namespace TemplateInsideNonTemplateClass
+
+namespace GH61959 {
+template <typename T0>
+concept C = (sizeof(T0) >= 4);
+
+template<typename...>
+struct Orig { };
+
+template<typename T>
+struct Orig<T> {
+ template<typename> requires C<T>
+ void f() { }
+
+ template<typename> requires true
+ void f() { }
+};
+
+template <typename...> struct Mod {};
+
+template <typename T1, typename T2>
+struct Mod<T1, T2> {
+ template <typename> requires C<T1>
+ constexpr static int f() { return 1; }
+
+ template <typename> requires C<T2>
+ constexpr static int f() { return 2; }
+};
+
+static_assert(Mod<int, char>::f<double>() == 1);
+static_assert(Mod<char, int>::f<double>() == 2);
+
+template<typename T>
+struct Outer {
+ template<typename ...>
+ struct Inner {};
+
+ template<typename U>
+ struct Inner<U> {
+ template<typename V>
+ void foo() requires C<U> && C<T> && C<V>{}
+ template<typename V>
+ void foo() requires true{}
+ };
+};
+
+void bar() {
+ Outer<int>::Inner<float> I;
+ I.foo<char>();
+}
+} // namespace GH61959
+
+
+namespace TemplateInsideTemplateInsideTemplate {
+template<typename T>
+concept C1 = false;
+
+template <unsigned I0>
+struct W0 {
+ template <unsigned I1>
+ struct W1 {
+ template <typename T>
+ struct F {
+ enum { value = 1 };
+ };
+
+ template <typename T>
+ requires C1<T>
+ struct F<T> {
+ enum { value = 2 };
+ };
+ };
+};
+
+static_assert(W0<0>::W1<1>::F<int>::value == 1);
+} // TemplateInsideTemplateInsideTemplate