Skip to content

Commit 2a2c228

Browse files
committed
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
1 parent 997a719 commit 2a2c228

File tree

15 files changed

+383
-61
lines changed

15 files changed

+383
-61
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ def FunctionTmpl
126126
FunctionDecl::TK_FunctionTemplate}],
127127
"function templates">;
128128

129+
def ClassTmpl : SubsetSubject<CXXRecord, [{S->getDescribedClassTemplate()}],
130+
"class templates">;
131+
129132
// FIXME: this hack is needed because DeclNodes.td defines the base Decl node
130133
// type to be a class, not a definition. This makes it impossible to create an
131134
// attribute subject which accepts a Decl. Normally, this is not a problem,
@@ -2391,6 +2394,16 @@ def Pascal : DeclOrTypeAttr {
23912394
let Documentation = [Undocumented];
23922395
}
23932396

2397+
def PreferredName : InheritableAttr {
2398+
let Spellings = [Clang<"preferred_name", /*AllowInC*/0>];
2399+
let Subjects = SubjectList<[ClassTmpl]>;
2400+
let Args = [TypeArgument<"TypedefType">];
2401+
let Documentation = [PreferredNameDocs];
2402+
let InheritEvenIfAlreadyPresent = 1;
2403+
let MeaningfulToClassTemplateDefinition = 1;
2404+
let TemplateDependent = 1;
2405+
}
2406+
23942407
def PreserveMost : DeclOrTypeAttr {
23952408
let Spellings = [Clang<"preserve_most">];
23962409
let Documentation = [PreserveMostDocs];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4471,6 +4471,30 @@ the old mangled name and the new code will use the new mangled name with tags.
44714471
}];
44724472
}
44734473

4474+
def PreferredNameDocs : Documentation {
4475+
let Category = DocCatDecl;
4476+
let Content = [{
4477+
The ``preferred_name`` attribute can be applied to a class template, and
4478+
specifies a preferred way of naming a specialization of the template. The
4479+
preferred name will be used whenever the corresponding template specialization
4480+
would otherwise be printed in a diagnostic or similar context.
4481+
4482+
The preferred name must be a typedef or type alias declaration that refers to a
4483+
specialization of the class template (not including any type qualifiers). In
4484+
general this requires the template to be declared at least twice. For example:
4485+
4486+
.. code-block:: c++
4487+
4488+
template<typename T> struct basic_string;
4489+
using string = basic_string<char>;
4490+
using wstring = basic_string<wchar_t>;
4491+
template<typename T> struct [[clang::preferred_name(string),
4492+
clang::preferred_name(wstring)]] basic_string {
4493+
// ...
4494+
};
4495+
}];
4496+
}
4497+
44744498
def PreserveMostDocs : Documentation {
44754499
let Category = DocCatCallingConvs;
44764500
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3941,6 +3941,9 @@ def note_protocol_decl : Note<
39413941
"protocol is declared here">;
39423942
def note_protocol_decl_undefined : Note<
39433943
"protocol %0 has no definition">;
3944+
def err_attribute_preferred_name_arg_invalid : Error<
3945+
"argument %0 to 'preferred_name' attribute is not a typedef for "
3946+
"a specialization of %1">;
39443947

39453948
// objc_designated_initializer attribute diagnostics.
39463949
def warn_objc_designated_init_missing_super_call : Warning<

clang/lib/AST/TypePrinter.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "clang/AST/PrettyPrinter.h"
1413
#include "clang/AST/ASTContext.h"
14+
#include "clang/AST/Attr.h"
1515
#include "clang/AST/Decl.h"
1616
#include "clang/AST/DeclBase.h"
1717
#include "clang/AST/DeclCXX.h"
1818
#include "clang/AST/DeclObjC.h"
1919
#include "clang/AST/DeclTemplate.h"
2020
#include "clang/AST/Expr.h"
2121
#include "clang/AST/NestedNameSpecifier.h"
22+
#include "clang/AST/PrettyPrinter.h"
2223
#include "clang/AST/TemplateBase.h"
2324
#include "clang/AST/TemplateName.h"
2425
#include "clang/AST/Type.h"
@@ -1348,6 +1349,14 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
13481349
}
13491350

13501351
void TypePrinter::printRecordBefore(const RecordType *T, raw_ostream &OS) {
1352+
// Print the preferred name if we have one for this type.
1353+
for (const auto *PNA : T->getDecl()->specific_attrs<PreferredNameAttr>()) {
1354+
if (declaresSameEntity(PNA->getTypedefType()->getAsCXXRecordDecl(),
1355+
T->getDecl()))
1356+
return printTypeSpec(
1357+
PNA->getTypedefType()->castAs<TypedefType>()->getDecl(), OS);
1358+
}
1359+
13511360
printTag(T->getDecl(), OS);
13521361
}
13531362

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,43 @@ static void handlePackedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
13801380
S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL;
13811381
}
13821382

1383+
static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
1384+
auto *RD = cast<CXXRecordDecl>(D);
1385+
ClassTemplateDecl *CTD = RD->getDescribedClassTemplate();
1386+
assert(CTD && "attribute does not appertain to this declaration");
1387+
1388+
ParsedType PT = AL.getTypeArg();
1389+
TypeSourceInfo *TSI = nullptr;
1390+
QualType T = S.GetTypeFromParser(PT, &TSI);
1391+
if (!TSI)
1392+
TSI = S.Context.getTrivialTypeSourceInfo(T, AL.getLoc());
1393+
1394+
if (!T.hasQualifiers() && T->getAs<TypedefType>()) {
1395+
// Find the template name, if this type names a template specialization.
1396+
const TemplateDecl *Template = nullptr;
1397+
if (const auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
1398+
T->getAsCXXRecordDecl())) {
1399+
Template = CTSD->getSpecializedTemplate();
1400+
} else if (const auto *TST = T->getAs<TemplateSpecializationType>()) {
1401+
while (TST && TST->isTypeAlias())
1402+
TST = TST->getAliasedType()->getAs<TemplateSpecializationType>();
1403+
if (TST)
1404+
Template = TST->getTemplateName().getAsTemplateDecl();
1405+
}
1406+
1407+
if (Template && declaresSameEntity(Template, CTD)) {
1408+
D->addAttr(::new (S.Context) PreferredNameAttr(S.Context, AL, TSI));
1409+
return;
1410+
}
1411+
}
1412+
1413+
S.Diag(AL.getLoc(), diag::err_attribute_preferred_name_arg_invalid)
1414+
<< T << CTD;
1415+
if (const auto *TT = T->getAs<TypedefType>())
1416+
S.Diag(TT->getDecl()->getLocation(), diag::note_entity_declared_at)
1417+
<< TT->getDecl();
1418+
}
1419+
13831420
static bool checkIBOutletCommon(Sema &S, Decl *D, const ParsedAttr &AL) {
13841421
// The IBOutlet/IBOutletCollection attributes only apply to instance
13851422
// variables or properties of Objective-C classes. The outlet must also
@@ -7778,6 +7815,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
77787815
case ParsedAttr::AT_Packed:
77797816
handlePackedAttr(S, D, AL);
77807817
break;
7818+
case ParsedAttr::AT_PreferredName:
7819+
handlePreferredName(S, D, AL);
7820+
break;
77817821
case ParsedAttr::AT_Section:
77827822
handleSectionAttr(S, D, AL);
77837823
break;

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3792,11 +3792,15 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
37923792
Decl->setLexicalDeclContext(ClassTemplate->getLexicalDeclContext());
37933793
}
37943794

3795-
if (Decl->getSpecializationKind() == TSK_Undeclared) {
3796-
MultiLevelTemplateArgumentList TemplateArgLists;
3797-
TemplateArgLists.addOuterTemplateArguments(Converted);
3798-
InstantiateAttrsForDecl(TemplateArgLists, ClassTemplate->getTemplatedDecl(),
3799-
Decl);
3795+
if (Decl->getSpecializationKind() == TSK_Undeclared &&
3796+
ClassTemplate->getTemplatedDecl()->hasAttrs()) {
3797+
InstantiatingTemplate Inst(*this, TemplateLoc, Decl);
3798+
if (!Inst.isInvalid()) {
3799+
MultiLevelTemplateArgumentList TemplateArgLists;
3800+
TemplateArgLists.addOuterTemplateArguments(Converted);
3801+
InstantiateAttrsForDecl(TemplateArgLists,
3802+
ClassTemplate->getTemplatedDecl(), Decl);
3803+
}
38003804
}
38013805

38023806
// Diagnose uses of this specialization.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,40 @@ static void instantiateDependentAMDGPUWavesPerEUAttr(
548548
S.addAMDGPUWavesPerEUAttr(New, Attr, MinExpr, MaxExpr);
549549
}
550550

551+
/// Determine whether the attribute A might be relevent to the declaration D.
552+
/// If not, we can skip instantiating it. The attribute may or may not have
553+
/// been instantiated yet.
554+
static bool isRelevantAttr(Sema &S, const Decl *D, const Attr *A) {
555+
// 'preferred_name' is only relevant to the matching specialization of the
556+
// template.
557+
if (const auto *PNA = dyn_cast<PreferredNameAttr>(A)) {
558+
QualType T = PNA->getTypedefType();
559+
const auto *RD = cast<CXXRecordDecl>(D);
560+
if (!T->isDependentType() && !RD->isDependentContext() &&
561+
!declaresSameEntity(T->getAsCXXRecordDecl(), RD))
562+
return false;
563+
for (const auto *ExistingPNA : D->specific_attrs<PreferredNameAttr>())
564+
if (S.Context.hasSameType(ExistingPNA->getTypedefType(),
565+
PNA->getTypedefType()))
566+
return false;
567+
return true;
568+
}
569+
570+
return true;
571+
}
572+
551573
void Sema::InstantiateAttrsForDecl(
552574
const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl,
553575
Decl *New, LateInstantiatedAttrVec *LateAttrs,
554576
LocalInstantiationScope *OuterMostScope) {
555577
if (NamedDecl *ND = dyn_cast<NamedDecl>(New)) {
578+
// FIXME: This function is called multiple times for the same template
579+
// specialization. We should only instantiate attributes that were added
580+
// since the previous instantiation.
556581
for (const auto *TmplAttr : Tmpl->attrs()) {
582+
if (!isRelevantAttr(*this, New, TmplAttr))
583+
continue;
584+
557585
// FIXME: If any of the special case versions from InstantiateAttrs become
558586
// applicable to template declaration, we'll need to add them here.
559587
CXXThisScopeRAII ThisScope(
@@ -562,7 +590,7 @@ void Sema::InstantiateAttrsForDecl(
562590

563591
Attr *NewAttr = sema::instantiateTemplateAttributeForDecl(
564592
TmplAttr, Context, *this, TemplateArgs);
565-
if (NewAttr)
593+
if (NewAttr && isRelevantAttr(*this, New, NewAttr))
566594
New->addAttr(NewAttr);
567595
}
568596
}
@@ -587,6 +615,9 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
587615
LateInstantiatedAttrVec *LateAttrs,
588616
LocalInstantiationScope *OuterMostScope) {
589617
for (const auto *TmplAttr : Tmpl->attrs()) {
618+
if (!isRelevantAttr(*this, New, TmplAttr))
619+
continue;
620+
590621
// FIXME: This should be generalized to more than just the AlignedAttr.
591622
const AlignedAttr *Aligned = dyn_cast<AlignedAttr>(TmplAttr);
592623
if (Aligned && Aligned->isAlignmentDependent()) {
@@ -709,7 +740,7 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
709740

710741
Attr *NewAttr = sema::instantiateTemplateAttribute(TmplAttr, Context,
711742
*this, TemplateArgs);
712-
if (NewAttr)
743+
if (NewAttr && isRelevantAttr(*this, New, TmplAttr))
713744
New->addAttr(NewAttr);
714745
}
715746
}

clang/test/PCH/decl-attrs.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t.a %s
2+
// RUN: %clang_cc1 -std=c++20 -include-pch %t.a %s -verify
3+
4+
#ifndef HEADER
5+
#define HEADER
6+
7+
namespace preferred_name {
8+
template<typename T> struct X;
9+
using Y = X<int>;
10+
using Z = X<float>;
11+
template<typename T> struct [[using clang: preferred_name(Y), preferred_name(Z)]] X {};
12+
Y y;
13+
}
14+
15+
#else
16+
17+
namespace preferred_name {
18+
Z z;
19+
20+
template<typename T> T forget(T t) { return t; }
21+
void f() {
22+
forget(y).foo(); // expected-error {{no member named 'foo' in 'preferred_name::Y'}}
23+
forget(z).foo(); // expected-error {{no member named 'foo' in 'preferred_name::Z'}}
24+
}
25+
}
26+
27+
#endif

clang/test/SemaTemplate/attributes.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,67 @@ namespace PR9049 {
6363
// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAR"
6464
template<typename T> [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations();
6565
void UseAnnotations() { HasAnnotations<int>(); }
66+
67+
namespace preferred_name {
68+
int x [[clang::preferred_name("frank")]]; // expected-error {{expected a type}}
69+
int y [[clang::preferred_name(int)]]; // expected-warning {{'preferred_name' attribute only applies to class templates}}
70+
struct [[clang::preferred_name(int)]] A; // expected-warning {{'preferred_name' attribute only applies to class templates}}
71+
template<typename T> struct [[clang::preferred_name(int)]] B; // expected-error {{argument 'int' to 'preferred_name' attribute is not a typedef for a specialization of 'B'}}
72+
template<typename T> struct C;
73+
using X = C<int>; // expected-note {{'X' declared here}}
74+
typedef C<float> Y;
75+
using Z = const C<double>; // expected-note {{'Z' declared here}}
76+
template<typename T> struct [[clang::preferred_name(C<int>)]] C; // expected-error {{argument 'C<int>' to 'preferred_name' attribute is not a typedef for a specialization of 'C'}}
77+
template<typename T> struct [[clang::preferred_name(X), clang::preferred_name(Y)]] C;
78+
template<typename T> struct [[clang::preferred_name(const X)]] C; // expected-error {{argument 'const preferred_name::X'}}
79+
template<typename T> struct [[clang::preferred_name(Z)]] C; // expected-error {{argument 'preferred_name::Z' (aka 'const C<double>')}}
80+
template<typename T> struct C {};
81+
82+
// CHECK: ClassTemplateDecl {{.*}} <line:[[@LINE-10]]:{{.*}} C
83+
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct C definition
84+
// CHECK: TemplateArgument type 'int'
85+
// CHECK-NOT: PreferredNameAttr
86+
// CHECK: PreferredNameAttr {{.*}} preferred_name::X
87+
// CHECK-NOT: PreferredNameAttr
88+
// CHECK: CXXRecordDecl
89+
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct C definition
90+
// CHECK: TemplateArgument type 'float'
91+
// CHECK-NOT: PreferredNameAttr
92+
// CHECK: PreferredNameAttr {{.*}} preferred_name::Y
93+
// CHECK-NOT: PreferredNameAttr
94+
// CHECK: CXXRecordDecl
95+
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct C definition
96+
// CHECK: TemplateArgument type 'double'
97+
// CHECK-NOT: PreferredNameAttr
98+
// CHECK: CXXRecordDecl
99+
100+
// Check this doesn't cause us to instantiate the same attribute multiple times.
101+
C<float> *cf1;
102+
C<float> *cf2;
103+
104+
void f(C<int> a, C<float> b, C<double> c) {
105+
auto p = a;
106+
auto q = b;
107+
auto r = c;
108+
p.f(); // expected-error {{no member named 'f' in 'preferred_name::X'}}
109+
q.f(); // expected-error {{no member named 'f' in 'preferred_name::Y'}}
110+
r.f(); // expected-error {{no member named 'f' in 'preferred_name::C<double>'}}
111+
}
112+
113+
template<typename T> struct D;
114+
using DInt = D<int>;
115+
template<typename T> struct __attribute__((__preferred_name__(DInt))) D {};
116+
template struct D<int>;
117+
int use_dint = D<int>().get(); // expected-error {{no member named 'get' in 'preferred_name::DInt'}}
118+
119+
template<typename T> struct MemberTemplate {
120+
template<typename U> struct Iter;
121+
using iterator = Iter<T>;
122+
using const_iterator = Iter<const T>;
123+
template<typename U>
124+
struct [[clang::preferred_name(iterator),
125+
clang::preferred_name(const_iterator)]] Iter {};
126+
};
127+
auto it = MemberTemplate<int>::Iter<const int>();
128+
int n = it; // expected-error {{no viable conversion from 'preferred_name::MemberTemplate<int>::const_iterator' to 'int'}}
129+
}

clang/utils/TableGen/ClangAttrEmitter.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,8 +1149,9 @@ namespace {
11491149
<< "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n";
11501150
OS << " ExprResult " << "Result = S.SubstExpr("
11511151
<< "A->get" << getUpperName() << "(), TemplateArgs);\n";
1152-
OS << " tempInst" << getUpperName() << " = "
1153-
<< "Result.getAs<Expr>();\n";
1152+
OS << " if (Result.isInvalid())\n";
1153+
OS << " return nullptr;\n";
1154+
OS << " tempInst" << getUpperName() << " = Result.get();\n";
11541155
OS << " }\n";
11551156
}
11561157

@@ -1202,7 +1203,9 @@ namespace {
12021203
<< "_end();\n";
12031204
OS << " for (; I != E; ++I, ++TI) {\n";
12041205
OS << " ExprResult Result = S.SubstExpr(*I, TemplateArgs);\n";
1205-
OS << " *TI = Result.getAs<Expr>();\n";
1206+
OS << " if (Result.isInvalid())\n";
1207+
OS << " return nullptr;\n";
1208+
OS << " *TI = Result.get();\n";
12061209
OS << " }\n";
12071210
OS << " }\n";
12081211
}
@@ -1273,8 +1276,16 @@ namespace {
12731276
OS << " return false;\n";
12741277
}
12751278

1279+
void writeTemplateInstantiation(raw_ostream &OS) const override {
1280+
OS << " " << getType() << " tempInst" << getUpperName() << " =\n";
1281+
OS << " S.SubstType(A->get" << getUpperName() << "Loc(), "
1282+
<< "TemplateArgs, A->getLoc(), A->getAttrName());\n";
1283+
OS << " if (!tempInst" << getUpperName() << ")\n";
1284+
OS << " return nullptr;\n";
1285+
}
1286+
12761287
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
1277-
OS << "A->get" << getUpperName() << "Loc()";
1288+
OS << "tempInst" << getUpperName();
12781289
}
12791290

12801291
void writePCHWrite(raw_ostream &OS) const override {
@@ -3319,12 +3330,13 @@ void EmitClangAttrTemplateInstantiateHelper(const std::vector<Record *> &Attrs,
33193330
for (auto const &ai : Args)
33203331
ai->writeTemplateInstantiation(OS);
33213332

3322-
OS << " return new (C) " << R.getName() << "Attr(C, *A";
3333+
OS << " return new (C) " << R.getName() << "Attr(C, *A";
33233334
for (auto const &ai : Args) {
33243335
OS << ", ";
33253336
ai->writeTemplateInstantiationArgs(OS);
33263337
}
3327-
OS << ");\n }\n";
3338+
OS << ");\n"
3339+
<< " }\n";
33283340
}
33293341
OS << " } // end switch\n"
33303342
<< " llvm_unreachable(\"Unknown attribute!\");\n"

0 commit comments

Comments
 (0)