alexander-shaposhnikov updated this revision to Diff 507560. alexander-shaposhnikov added a comment.
Add more tests. P.S. we already have tests with self-friends (in concepts.cpp), the test from Richard's comment is also included (struct S12) (in a slightly simplified form) Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D146178/new/ https://reviews.llvm.org/D146178 Files: clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/SemaTemplate/concepts-out-of-line-def.cpp
Index: clang/test/SemaTemplate/concepts-out-of-line-def.cpp =================================================================== --- clang/test/SemaTemplate/concepts-out-of-line-def.cpp +++ clang/test/SemaTemplate/concepts-out-of-line-def.cpp @@ -127,3 +127,153 @@ 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 Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1653,33 +1653,12 @@ << 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); @@ -1689,16 +1668,37 @@ 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) { + 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 @@ -1709,15 +1709,15 @@ 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; } Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -1296,9 +1296,11 @@ // // We check the return type and template parameter lists for function // templates first; the remaining checks follow. + bool SameTemplateParameterList = TemplateParameterListsAreEqual( - NewTemplate->getTemplateParameters(), - OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch); + NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate, + OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch, + SourceLocation(), false /* PartialOrdering */); bool SameReturnType = Context.hasSameType(Old->getDeclaredReturnType(), New->getDeclaredReturnType()); // FIXME(GH58571): Match template parameter list even for non-constrained Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -768,26 +768,56 @@ }; } // namespace +// Check if the declaration context is inside an implicit full template +// specialization. I.e. after skipping inner member classes if the declaration +// context is an implicit full template specialization. +static bool IsInsideImplicitFullTemplateSpecialization(const DeclContext *DC) { + auto *CTSDecl = dyn_cast_or_null<ClassTemplateSpecializationDecl>( + DC->getOuterLexicalRecordContext()); + return CTSDecl && !isa<ClassTemplatePartialSpecializationDecl>(CTSDecl) && + !CTSDecl->isExplicitInstantiationOrSpecialization(); +} + +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); + 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 (IsInsideImplicitFullTemplateSpecialization( + Old->getLexicalDeclContext())) { + if (const Expr *SubstConstr = + SubstituteConstraintExpression(*this, Old, OldConstr)) + OldConstr = SubstConstr; + else + return false; + } + if (IsInsideImplicitFullTemplateSpecialization( + New->getLexicalDeclContext())) { + if (const Expr *SubstConstr = + SubstituteConstraintExpression(*this, New, NewConstr)) + NewConstr = SubstConstr; + else + return false; } }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits