Commit 2a2c228c authored by Richard Smith's avatar Richard Smith
Browse files

Add new 'preferred_name' attribute.

This attribute permits a typedef to be associated with a class template
specialization as a preferred way of naming that class template
specialization. This permits us to specify that (for example) the
preferred way to express 'std::basic_string<char>' is as 'std::string'.

The attribute is applied to the various class templates in libc++ that have
corresponding well-known typedef names.

This is a re-commit. The previous commit was reverted because it exposed
a pre-existing bug that has since been fixed / worked around; see
PR48434.

Differential Revision: https://reviews.llvm.org/D91311
parent 997a719d
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -126,6 +126,9 @@ def FunctionTmpl
                                 FunctionDecl::TK_FunctionTemplate}],
                    "function templates">;

def ClassTmpl : SubsetSubject<CXXRecord, [{S->getDescribedClassTemplate()}],
                              "class templates">;

// FIXME: this hack is needed because DeclNodes.td defines the base Decl node
// type to be a class, not a definition. This makes it impossible to create an
// attribute subject which accepts a Decl. Normally, this is not a problem,
@@ -2391,6 +2394,16 @@ def Pascal : DeclOrTypeAttr {
  let Documentation = [Undocumented];
}

def PreferredName : InheritableAttr {
  let Spellings = [Clang<"preferred_name", /*AllowInC*/0>];
  let Subjects = SubjectList<[ClassTmpl]>;
  let Args = [TypeArgument<"TypedefType">];
  let Documentation = [PreferredNameDocs];
  let InheritEvenIfAlreadyPresent = 1;
  let MeaningfulToClassTemplateDefinition = 1;
  let TemplateDependent = 1;
}

def PreserveMost : DeclOrTypeAttr {
  let Spellings = [Clang<"preserve_most">];
  let Documentation = [PreserveMostDocs];
+24 −0
Original line number Diff line number Diff line
@@ -4471,6 +4471,30 @@ the old mangled name and the new code will use the new mangled name with tags.
  }];
}

def PreferredNameDocs : Documentation {
  let Category = DocCatDecl;
  let Content = [{
The ``preferred_name`` attribute can be applied to a class template, and
specifies a preferred way of naming a specialization of the template. The
preferred name will be used whenever the corresponding template specialization
would otherwise be printed in a diagnostic or similar context.

The preferred name must be a typedef or type alias declaration that refers to a
specialization of the class template (not including any type qualifiers). In
general this requires the template to be declared at least twice. For example:

.. code-block:: c++

  template<typename T> struct basic_string;
  using string = basic_string<char>;
  using wstring = basic_string<wchar_t>;
  template<typename T> struct [[clang::preferred_name(string),
                                clang::preferred_name(wstring)]] basic_string {
    // ...
  };
  }];
}

def PreserveMostDocs : Documentation {
  let Category = DocCatCallingConvs;
  let Content = [{
+3 −0
Original line number Diff line number Diff line
@@ -3941,6 +3941,9 @@ def note_protocol_decl : Note<
  "protocol is declared here">;
def note_protocol_decl_undefined : Note<
  "protocol %0 has no definition">;
def err_attribute_preferred_name_arg_invalid : Error<
  "argument %0 to 'preferred_name' attribute is not a typedef for "
  "a specialization of %1">;
// objc_designated_initializer attribute diagnostics.
def warn_objc_designated_init_missing_super_call : Warning<
+10 −1
Original line number Diff line number Diff line
@@ -10,8 +10,8 @@
//
//===----------------------------------------------------------------------===//

#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -19,6 +19,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
@@ -1348,6 +1349,14 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
}

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);
  }

  printTag(T->getDecl(), OS);
}

+40 −0
Original line number Diff line number Diff line
@@ -1380,6 +1380,43 @@ static void handlePackedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
    S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL;
}

static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
  auto *RD = cast<CXXRecordDecl>(D);
  ClassTemplateDecl *CTD = RD->getDescribedClassTemplate();
  assert(CTD && "attribute does not appertain to this declaration");

  ParsedType PT = AL.getTypeArg();
  TypeSourceInfo *TSI = nullptr;
  QualType T = S.GetTypeFromParser(PT, &TSI);
  if (!TSI)
    TSI = S.Context.getTrivialTypeSourceInfo(T, AL.getLoc());

  if (!T.hasQualifiers() && T->getAs<TypedefType>()) {
    // Find the template name, if this type names a template specialization.
    const TemplateDecl *Template = nullptr;
    if (const auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
            T->getAsCXXRecordDecl())) {
      Template = CTSD->getSpecializedTemplate();
    } else if (const auto *TST = T->getAs<TemplateSpecializationType>()) {
      while (TST && TST->isTypeAlias())
        TST = TST->getAliasedType()->getAs<TemplateSpecializationType>();
      if (TST)
        Template = TST->getTemplateName().getAsTemplateDecl();
    }

    if (Template && declaresSameEntity(Template, CTD)) {
      D->addAttr(::new (S.Context) PreferredNameAttr(S.Context, AL, TSI));
      return;
    }
  }

  S.Diag(AL.getLoc(), diag::err_attribute_preferred_name_arg_invalid)
      << T << CTD;
  if (const auto *TT = T->getAs<TypedefType>())
    S.Diag(TT->getDecl()->getLocation(), diag::note_entity_declared_at)
        << TT->getDecl();
}

static bool checkIBOutletCommon(Sema &S, Decl *D, const ParsedAttr &AL) {
  // The IBOutlet/IBOutletCollection attributes only apply to instance
  // variables or properties of Objective-C classes.  The outlet must also
@@ -7778,6 +7815,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
  case ParsedAttr::AT_Packed:
    handlePackedAttr(S, D, AL);
    break;
  case ParsedAttr::AT_PreferredName:
    handlePreferredName(S, D, AL);
    break;
  case ParsedAttr::AT_Section:
    handleSectionAttr(S, D, AL);
    break;
Loading