Allow dependent alias template specializations in the preferred_name
authorRichard Smith <richard@metafoo.co.uk>
Tue, 5 Jan 2021 23:32:44 +0000 (15:32 -0800)
committerRichard Smith <richard@metafoo.co.uk>
Tue, 5 Jan 2021 23:33:51 +0000 (15:33 -0800)
attribute.

This was intended to work, but didn't match the checks because these
types are modeled as TemplateSpecializationTypes not TypedefTypes.

clang/include/clang/AST/Type.h
clang/lib/AST/TypePrinter.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/test/SemaTemplate/attributes.cpp

index 684005c..143a05c 100644 (file)
@@ -2093,6 +2093,7 @@ public:
   bool isAtomicType() const;                    // C11 _Atomic()
   bool isUndeducedAutoType() const;             // C++11 auto or
                                                 // C++14 decltype(auto)
+  bool isTypedefNameType() const;               // typedef or alias template
 
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
   bool is##Id##Type() const;
@@ -7066,6 +7067,15 @@ inline bool Type::isOverloadableType() const {
   return isDependentType() || isRecordType() || isEnumeralType();
 }
 
+/// Determines whether this type is written as a typedef-name.
+inline bool Type::isTypedefNameType() const {
+  if (getAs<TypedefType>())
+    return true;
+  if (auto *TST = getAs<TemplateSpecializationType>())
+    return TST->isTypeAlias();
+  return false;
+}
+
 /// Determines whether this type can decay to a pointer type.
 inline bool Type::canDecayToPointerType() const {
   return isFunctionType() || isArrayType();
index 54c4512..d1882ac 100644 (file)
@@ -116,6 +116,8 @@ namespace {
     static bool canPrefixQualifiers(const Type *T, bool &NeedARCStrongQualifier);
     void spaceBeforePlaceHolder(raw_ostream &OS);
     void printTypeSpec(NamedDecl *D, raw_ostream &OS);
+    void printTemplateId(const TemplateSpecializationType *T, raw_ostream &OS,
+                         bool FullyQualify);
 
     void printBefore(QualType T, raw_ostream &OS);
     void printAfter(QualType T, raw_ostream &OS);
@@ -1352,9 +1354,17 @@ void TypePrinter::printRecordBefore(const RecordType *T, raw_ostream &OS) {
   // Print the preferred name if we have one for this type.
   for (const auto *PNA : T->getDecl()->specific_attrs<PreferredNameAttr>()) {
     if (declaresSameEntity(PNA->getTypedefType()->getAsCXXRecordDecl(),
-                           T->getDecl()))
-      return printTypeSpec(
-          PNA->getTypedefType()->castAs<TypedefType>()->getDecl(), OS);
+                           T->getDecl())) {
+      // Find the outermost typedef or alias template.
+      QualType T = PNA->getTypedefType();
+      while (true) {
+        if (auto *TT = dyn_cast<TypedefType>(T))
+          return printTypeSpec(TT->getDecl(), OS);
+        if (auto *TST = dyn_cast<TemplateSpecializationType>(T))
+          return printTemplateId(TST, OS, /*FullyQualify=*/true);
+        T = T->getLocallyUnqualifiedSingleStepDesugaredType();
+      }
+    }
   }
 
   printTag(T->getDecl(), OS);
@@ -1416,20 +1426,32 @@ void TypePrinter::printSubstTemplateTypeParmPackAfter(
   printTemplateTypeParmAfter(T->getReplacedParameter(), OS);
 }
 
-void TypePrinter::printTemplateSpecializationBefore(
-                                            const TemplateSpecializationType *T,
-                                            raw_ostream &OS) {
+void TypePrinter::printTemplateId(const TemplateSpecializationType *T,
+                                  raw_ostream &OS, bool FullyQualify) {
   IncludeStrongLifetimeRAII Strong(Policy);
-  T->getTemplateName().print(OS, Policy);
 
-  const TemplateParameterList *TPL = nullptr;
-  if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl())
-    TPL = TD->getTemplateParameters();
+  TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl();
+  if (FullyQualify && TD) {
+    if (!Policy.SuppressScope)
+      AppendScope(TD->getDeclContext(), OS, TD->getDeclName());
+
+    IdentifierInfo *II = TD->getIdentifier();
+    OS << II->getName();
+  } else {
+    T->getTemplateName().print(OS, Policy);
+  }
 
+  const TemplateParameterList *TPL = TD ? TD->getTemplateParameters() : nullptr;
   printTemplateArgumentList(OS, T->template_arguments(), Policy, TPL);
   spaceBeforePlaceHolder(OS);
 }
 
+void TypePrinter::printTemplateSpecializationBefore(
+                                            const TemplateSpecializationType *T,
+                                            raw_ostream &OS) {
+  printTemplateId(T, OS, false);
+}
+
 void TypePrinter::printTemplateSpecializationAfter(
                                             const TemplateSpecializationType *T,
                                             raw_ostream &OS) {}
index c836031..0872cc6 100644 (file)
@@ -1393,7 +1393,7 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
   if (!TSI)
     TSI = S.Context.getTrivialTypeSourceInfo(T, AL.getLoc());
 
-  if (!T.hasQualifiers() && T->getAs<TypedefType>()) {
+  if (!T.hasQualifiers() && T->isTypedefNameType()) {
     // Find the template name, if this type names a template specialization.
     const TemplateDecl *Template = nullptr;
     if (const auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
index 3f52412..b0bcca5 100644 (file)
@@ -126,4 +126,10 @@ namespace preferred_name {
   };
   auto it = MemberTemplate<int>::Iter<const int>();
   int n = it; // expected-error {{no viable conversion from 'preferred_name::MemberTemplate<int>::const_iterator' to 'int'}}
+
+  template<int A, int B, typename ...T> struct Foo;
+  template<typename ...T> using Bar = Foo<1, 2, T...>;
+  template<int A, int B, typename ...T> struct [[clang::preferred_name(::preferred_name::Bar<T...>)]] Foo {};
+  Foo<1, 2, int, float>::nosuch x; // expected-error {{no type named 'nosuch' in 'preferred_name::Bar<int, float>'}}
 }
+::preferred_name::Foo<1, 2, int, float>::nosuch x; // expected-error {{no type named 'nosuch' in 'preferred_name::Bar<int, float>'}}