alexander-shaposhnikov updated this revision to Diff 503750.
alexander-shaposhnikov added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145034/new/

https://reviews.llvm.org/D145034

Files:
  clang/include/clang/Parse/Parser.h
  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,129 @@
+// 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 {
+  static 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> {
+  static constexpr int specialization();
+};
+
+template <class T, class U>
+  requires(sizeof(T) == sizeof(int))
+struct S<T, U> {
+  static 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
+
+namespace multiple_template_parameter_lists {
+
+template <class Outer>
+struct S {
+  template <class Inner>
+  static constexpr int primary(Inner);
+};
+
+template <class Outer>
+template <class Inner>
+constexpr int S<Outer>::primary(Inner) {
+  return PRIMARY;
+};
+
+template <Concept Outer>
+struct S<Outer> {
+  template <class Inner>
+  static constexpr int specialization(Inner);
+};
+
+template <Concept Outer>
+template <class Inner>
+constexpr int S<Outer>::specialization(Inner) { return SPECIALIZATION_CONCEPT; }
+
+template <class Outer>
+  requires(sizeof(Outer) == sizeof(int))
+struct S<Outer> {
+  template <class Inner>
+  static constexpr int specialization(Inner);
+};
+
+template <class Outer>
+  requires(sizeof(Outer) == sizeof(int))
+template <class Inner>
+constexpr int S<Outer>::specialization(Inner) { return SPECIALIZATION_REQUIRES; }
+
+static_assert(S<char>::primary("str") == PRIMARY);
+static_assert(S<XY>::specialization("str") == SPECIALIZATION_CONCEPT);
+static_assert(S<int>::specialization("str") == SPECIALIZATION_REQUIRES);
+
+} // namespace multiple_template_parameter_lists
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
@@ -3,7 +3,7 @@
 template<typename T, int N>
 struct A;
 
-template<typename T> // expected-note{{previous template declaration}}
+template<typename T>
 struct A<T*, 2> {
   void f0();
   void f1();
@@ -15,11 +15,10 @@
   void g0();
 };
 
-// FIXME: We should probably give more precise diagnostics here, but the
-// diagnostics we give aren't terrible.
-// FIXME: why not point to the first parameter that's "too many"?
-template<typename T, int N> // expected-error{{too many template parameters}}
-void A<T*, 2>::f0() { }
+// FIXME: We should produce diagnostics pointing out the
+// non-matching candidates.
+template<typename T, int N>
+void A<T*, 2>::f0() { } // expected-error{{does not refer into a class, class template or class template partial specialization}}
 
 template<typename T, int N>
 void A<T, N>::f1() { } // expected-error{{out-of-line definition}}
Index: clang/lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -99,34 +99,52 @@
         if (ClassTemplateDecl *ClassTemplate
               = dyn_cast_or_null<ClassTemplateDecl>(
                             SpecType->getTemplateName().getAsTemplateDecl())) {
-          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();
+          QualType ContextType =
+              Context.getCanonicalType(QualType(SpecType, 0));
+
+          // FIXME: 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. Currently removing this fallback would change the
+          // diagnostic output for invalid code in a number of tests.
+          ClassTemplatePartialSpecializationDecl *PartialSpec = nullptr;
+          ArrayRef<TemplateParameterList *> TemplateParamLists =
+              SS.getTemplateParamLists();
+          if (!TemplateParamLists.empty()) {
+            unsigned Depth = ClassTemplate->getTemplateParameters()->getDepth();
+            auto L = find_if(TemplateParamLists,
+                             [Depth](TemplateParameterList *TPL) {
+                               return TPL->getDepth() == Depth;
+                             });
+            if (L != TemplateParamLists.end()) {
+              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
@@ -3380,6 +3380,8 @@
         goto DoneWithDeclSpec;
 
       CXXScopeSpec SS;
+      if (TemplateInfo.TemplateParams)
+        SS.setTemplateParamLists(*TemplateInfo.TemplateParams);
       Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
                                                    Tok.getAnnotationRange(),
                                                    SS);
@@ -3475,7 +3477,8 @@
                                      &SS) &&
           isConstructorDeclarator(/*Unqualified=*/false,
                                   /*DeductionGuide=*/false,
-                                  DS.isFriendSpecified()))
+                                  DS.isFriendSpecified(),
+                                  &TemplateInfo))
         goto DoneWithDeclSpec;
 
       // C++20 [temp.spec] 13.9/6.
@@ -4952,6 +4955,7 @@
     assert(TemplateInfo.TemplateParams && "no template parameters");
     TParams = MultiTemplateParamsArg(TemplateInfo.TemplateParams->data(),
                                      TemplateInfo.TemplateParams->size());
+    SS.setTemplateParamLists(TParams);
   }
 
   if (!Name && TUK != Sema::TUK_Definition) {
@@ -5674,11 +5678,15 @@
 }
 
 bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
-                                     DeclSpec::FriendSpecified IsFriend) {
+                                     DeclSpec::FriendSpecified IsFriend,
+                                     const ParsedTemplateInfo *TemplateInfo) {
   TentativeParsingAction TPA(*this);
 
   // Parse the C++ scope specifier.
   CXXScopeSpec SS;
+  if (TemplateInfo && TemplateInfo->TemplateParams)
+    SS.setTemplateParamLists(*TemplateInfo->TemplateParams);
+
   if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
                                      /*ObjectHasErrors=*/false,
                                      /*EnteringContext=*/true)) {
@@ -6070,6 +6078,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();
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -2513,7 +2513,8 @@
   /// this is a constructor declarator.
   bool isConstructorDeclarator(
       bool Unqualified, bool DeductionGuide = false,
-      DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No);
+      DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No,
+      const ParsedTemplateInfo *TemplateInfo = nullptr);
 
   /// Specifies the context in which type-id/expression
   /// disambiguation will occur.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D145034: ... Alexander Shaposhnikov via Phabricator via cfe-commits
    • [PATCH] D145... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D145... Shafik Yaghmour via Phabricator via cfe-commits
    • [PATCH] D145... Alexander Shaposhnikov via Phabricator via cfe-commits
    • [PATCH] D145... Alexander Shaposhnikov via Phabricator via cfe-commits
    • [PATCH] D145... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D145... Alexander Shaposhnikov via Phabricator via cfe-commits

Reply via email to