alexander-shaposhnikov created this revision. alexander-shaposhnikov added reviewers: rsmith, aaron.ballman. alexander-shaposhnikov created this object with visibility "All Users". Herald added a project: All. alexander-shaposhnikov requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
This diff starts fixing our handling of out-of-line definitions of constrained templates. Initially it was motivated by https://github.com/llvm/llvm-project/issues/49620 . The current diff doesn't fully address those issues, more changes are required to make things work for the case where multiple template parameter lists are attached to the declaration, this will be done in a follow-up diff. Test plan: 1/ ninja check-all 2/ Bootstrapped Clang passes all the tests 3/ Internal testing (built a few large projects (that use templates extensively) with the bootstrapped version of Clang) Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D145034 Files: clang/include/clang/Sema/DeclSpec.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Sema/SemaCXXScopeSpec.cpp clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp clang/test/SemaTemplate/concepts-out-of-line-def.cpp
Index: clang/test/SemaTemplate/concepts-out-of-line-def.cpp =================================================================== --- /dev/null +++ clang/test/SemaTemplate/concepts-out-of-line-def.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// expected-no-diagnostics + +static constexpr int PRIMARY = 0; +static constexpr int SPECIALIZATION_CONCEPT = 1; +static constexpr int SPECIALIZATION_REQUIRES = 2; + +template <class T, class U> +struct S { + constexpr int primary(); +}; + +template <class T, class U> +constexpr int S<T, U>::primary() { return PRIMARY; }; + +template <class T> +concept Concept = (sizeof(T) >= 2 * sizeof(int)); + +template <Concept C, class U> +struct S<C, U> { + constexpr int specialization(); +}; + +template <class T, class U> +requires (sizeof(T) == sizeof(int)) +struct S<T, U> { + constexpr int specialization(); +}; + + +template <Concept C, class U> +constexpr int S<C, U>::specialization() { return SPECIALIZATION_CONCEPT; } + + +template <class T, class U> +requires (sizeof(T) == sizeof(int)) +constexpr int S<T, U>::specialization() { return SPECIALIZATION_REQUIRES; } + +struct XY { + int x; + int y; +}; + +static_assert (S<char, double>().primary() == PRIMARY); +static_assert (S<XY, double>().specialization() == SPECIALIZATION_CONCEPT); +static_assert (S<int, double>().specialization() == SPECIALIZATION_REQUIRES); + Index: clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp =================================================================== --- clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp +++ clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp @@ -1,4 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +// XFAIL: * +// NOTE: This test is marked XFAIL until the diagnostics of +// too many template parameters is fixed. template<typename T, int N> struct A; Index: clang/lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- clang/lib/Sema/SemaCXXScopeSpec.cpp +++ clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -102,31 +102,44 @@ QualType ContextType = Context.getCanonicalType(QualType(SpecType, 0)); - // If the type of the nested name specifier is the same as the - // injected class name of the named class template, we're entering - // into that class template definition. - QualType Injected - = ClassTemplate->getInjectedClassNameSpecialization(); - if (Context.hasSameType(Injected, ContextType)) - return ClassTemplate->getTemplatedDecl(); + // FIXME: currently only the case of ParamLists containing a single + // element is supported. The fallback on the search of partial + // specialization using ContextType should be eventually removed since + // it doesn't handle the case of constrained template parameters + // correctly. + ClassTemplatePartialSpecializationDecl *PartialSpec = nullptr; + ArrayRef<TemplateParameterList*> TemplateParamLists = SS.getTemplateParamLists(); + if (TemplateParamLists.size() == 1) { + // FIXME: pick the correct template parameter list based on NNS, SS + // and getCurScope(). + TemplateParameterList *L = TemplateParamLists[0]; + void *Pos = nullptr; + PartialSpec = ClassTemplate->findPartialSpecialization( + SpecType->template_arguments(), L, Pos); + } else { + PartialSpec = ClassTemplate->findPartialSpecialization(ContextType); + } - // If the type of the nested name specifier is the same as the - // type of one of the class template's class template partial - // specializations, we're entering into the definition of that - // class template partial specialization. - if (ClassTemplatePartialSpecializationDecl *PartialSpec - = ClassTemplate->findPartialSpecialization(ContextType)) { + if (PartialSpec) { // A declaration of the partial specialization must be visible. // We can always recover here, because this only happens when we're // entering the context, and that can't happen in a SFINAE context. - assert(!isSFINAEContext() && - "partial specialization scope specifier in SFINAE context?"); + assert(!isSFINAEContext() && "partial specialization scope " + "specifier in SFINAE context?"); if (!hasReachableDefinition(PartialSpec)) diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec, MissingImportKind::PartialSpecialization, - /*Recover*/true); + true); return PartialSpec; } + + // If the type of the nested name specifier is the same as the + // injected class name of the named class template, we're entering + // into that class template definition. + QualType Injected = + ClassTemplate->getInjectedClassNameSpecialization(); + if (Context.hasSameType(Injected, ContextType)) + return ClassTemplate->getTemplatedDecl(); } } else if (const RecordType *RecordT = NNSType->getAs<RecordType>()) { // The nested name specifier refers to a member of a class template. Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -1674,6 +1674,9 @@ ColonProtectionRAIIObject X(*this); CXXScopeSpec Spec; + if (TemplateInfo.TemplateParams) + Spec.setTemplateParamLists(*TemplateInfo.TemplateParams); + bool HasValidSpec = true; if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -6070,6 +6070,7 @@ bool EnteringContext = D.getContext() == DeclaratorContext::File || D.getContext() == DeclaratorContext::Member; CXXScopeSpec SS; + SS.setTemplateParamLists(D.getTemplateParameterLists()); ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, EnteringContext); Index: clang/include/clang/Sema/DeclSpec.h =================================================================== --- clang/include/clang/Sema/DeclSpec.h +++ clang/include/clang/Sema/DeclSpec.h @@ -65,6 +65,7 @@ class CXXScopeSpec { SourceRange Range; NestedNameSpecifierLocBuilder Builder; + ArrayRef<TemplateParameterList *> TemplateParamLists; public: SourceRange getRange() const { return Range; } @@ -74,6 +75,13 @@ SourceLocation getBeginLoc() const { return Range.getBegin(); } SourceLocation getEndLoc() const { return Range.getEnd(); } + void setTemplateParamLists(ArrayRef<TemplateParameterList *> L) { + TemplateParamLists = L; + } + ArrayRef<TemplateParameterList *> getTemplateParamLists() const { + return TemplateParamLists; + } + /// Retrieve the representation of the nested-name-specifier. NestedNameSpecifier *getScopeRep() const { return Builder.getRepresentation();
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits