diff options
-rw-r--r-- | clang/include/clang/AST/DeclTemplate.h | 6 | ||||
-rw-r--r-- | clang/include/clang/Sema/Template.h | 18 | ||||
-rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 51 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateDeduction.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 47 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 61 | ||||
-rw-r--r-- | clang/test/SemaTemplate/concepts-friends.cpp | 24 | ||||
-rw-r--r-- | clang/test/SemaTemplate/concepts-out-of-line-def.cpp | 272 | ||||
-rw-r--r-- | clang/test/SemaTemplate/concepts.cpp | 98 |
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 |