Author: Erich Keane Date: 2023-05-02T08:09:35-07:00 New Revision: 3e850a6eea5277082a0b7b701754c86530d25c40
URL: https://github.com/llvm/llvm-project/commit/3e850a6eea5277082a0b7b701754c86530d25c40 DIFF: https://github.com/llvm/llvm-project/commit/3e850a6eea5277082a0b7b701754c86530d25c40.diff LOG: Revert "[Clang][Sema] Fix comparison of constraint expressions" This reverts commit e3b1083e00e62f5d157d15cb8c63a1c3dfdf12e2. This was reverted because it breaks a number of libstdc++ examples, AND required a workaround that causes hiding of legitimate bugs. Added: Modified: clang/include/clang/AST/DeclTemplate.h clang/include/clang/Sema/Template.h clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/SemaTemplate/concepts-friends.cpp clang/test/SemaTemplate/concepts-out-of-line-def.cpp clang/test/SemaTemplate/concepts.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 7cd505218f2b9..3677335fa176f 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -2309,15 +2309,9 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl { 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 1de2cc6917b42..48e8b78311e12 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -232,21 +232,9 @@ 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(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}); - } + void replaceInnermostTemplateArguments(ArgList Args) { + assert(TemplateArgumentLists.size() > 0 && "Replacing in an empty list?"); + TemplateArgumentLists[0].Args = 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 f208cdbd1d87d..328d66bf33afa 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -721,7 +721,7 @@ CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, ND, /*Final=*/false, /*Innermost=*/nullptr, /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true, SkipForSpecialization); - return MLTAL.getNumLevels(); + return MLTAL.getNumSubstitutedLevels(); } namespace { @@ -752,44 +752,27 @@ 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) { - 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; + 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(); + } } llvm::FoldingSetNodeID ID1, ID2; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index a870671c26216..f783a996109d9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1296,7 +1296,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, NewTemplate->getTemplateParameters(), OldTemplate, + NewTemplate->getTemplateParameters(), 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 59d60f135bf50..d60c75c319894 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2881,7 +2881,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(Template, CanonicalDeducedArgs); + MLTAL.replaceInnermostTemplateArguments(CanonicalDeducedArgs); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, Info.getLocation(), diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 85c2b72c1046f..526f19b8aedcf 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -131,14 +131,6 @@ 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, @@ -216,21 +208,6 @@ 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, @@ -241,10 +218,15 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec, "Outer template not instantiated?"); if (ClassTemplate->isMemberSpecialization()) return Response::Done(); - if (ForConstraintInstantiation) + if (ForConstraintInstantiation) { + QualType RecordType = Context.getTypeDeclType(Rec); + QualType Injected = cast<InjectedClassNameType>(RecordType) + ->getInjectedSpecializationType(); + const auto *InjectedType = cast<TemplateSpecializationType>(Injected); Result.addOuterTemplateArguments(const_cast<CXXRecordDecl *>(Rec), - ClassTemplate->getInjectedTemplateArgs(), + InjectedType->template_arguments(), /*Final=*/false); + } } bool IsFriend = Rec->getFriendObjectKind() || @@ -324,10 +306,6 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( 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, @@ -340,8 +318,6 @@ 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 644eed56e1019..444162ef5df62 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1653,12 +1653,33 @@ 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); @@ -1668,38 +1689,16 @@ 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) { - 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; - + if (PrevClassTemplate) 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 +1709,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 6fab572e686c0..3a9b308a65c5d 100644 --- a/clang/test/SemaTemplate/concepts-friends.cpp +++ b/clang/test/SemaTemplate/concepts-friends.cpp @@ -441,27 +441,3 @@ namespace NTTP { templ_func<1>(u2); } } - - -namespace FrineOfFriend { - -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 FrineOfFriend diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp index 00c8097d575d0..222b78e0d22f7 100644 --- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp +++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp @@ -127,220 +127,3 @@ 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 diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index e97db046e7444..d28c2b22bd045 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -816,62 +816,3 @@ 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; -}; -} // 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>(); -} -} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits