alexander-shaposhnikov updated this revision to Diff 502067. alexander-shaposhnikov added a comment.
1/ Add support for out-of-line definitions of member enums + add tests 2/ Updated comments DeclSpec.h Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D145034/new/ 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,87 @@ +// 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> +concept Concept = (sizeof(T) >= 2 * sizeof(int)); + +struct XY { + int x; + int y; +}; + +namespace members { + +template <class T, class U> struct S { + constexpr int primary(); +}; + +template <class T, class U> constexpr int S<T, U>::primary() { + return PRIMARY; +}; + +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; +} + +static_assert(S<char, double>().primary() == PRIMARY); +static_assert(S<XY, double>().specialization() == SPECIALIZATION_CONCEPT); +static_assert(S<int, double>().specialization() == SPECIALIZATION_REQUIRES); + +} // namespace members + +namespace enumerations { + +template <class T, class U> struct S { + enum class E : int; +}; + +template <class T, class U> enum class S<T, U>::E { Value = PRIMARY }; + +template <Concept C, class U> struct S<C, U> { + enum class E : int; +}; + +template <Concept C, class U> +enum class S<C, U>::E { + Value = SPECIALIZATION_CONCEPT +}; + +template <class T, class U> + requires(sizeof(T) == sizeof(int)) +struct S<T, U> { + enum class E : int; +}; + +template <class T, class U> + requires(sizeof(T) == sizeof(int)) +enum class S<T, U>::E { + Value = SPECIALIZATION_REQUIRES +}; + +static_assert(static_cast<int>(S<char, double>::E::Value) == PRIMARY); +static_assert(static_cast<int>(S<XY, double>::E::Value) == + SPECIALIZATION_CONCEPT); +static_assert(static_cast<int>(S<int, double>::E::Value) == + SPECIALIZATION_REQUIRES); + +} // namespace enumerations 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 @@ -4952,6 +4952,7 @@ assert(TemplateInfo.TemplateParams && "no template parameters"); TParams = MultiTemplateParamsArg(TemplateInfo.TemplateParams->data(), TemplateInfo.TemplateParams->size()); + SS.setTemplateParamLists(TParams); } if (!Name && TUK != Sema::TUK_Definition) { @@ -6070,6 +6071,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 @@ -62,9 +62,18 @@ /// often used as if it meant "present". /// /// The actual scope is described by getScopeRep(). +/// +/// If the kind of getScopeRep() is TypeSpec then TemplateParamLists may be empty +/// or contain the template parameter lists attached to the current declaration. +/// Consider the following example: +/// template <class T> void SomeType<T>::some_method() {} +/// If CXXScopeSpec refers to SomeType<T> then TemplateParamLists will contain +/// a single element referring to template <class T>. + class CXXScopeSpec { SourceRange Range; NestedNameSpecifierLocBuilder Builder; + ArrayRef<TemplateParameterList *> TemplateParamLists; public: SourceRange getRange() const { return Range; } @@ -74,6 +83,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