rsmith created this revision. rsmith added reviewers: aaron.ballman, EricWF. Herald added a subscriber: jdoerfert. Herald added projects: clang, libc++. Herald added a reviewer: libc++. rsmith requested review of this revision.
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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D91311 Files: clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/AST/TypePrinter.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/SemaTemplate/attributes.cpp libcxx/include/__config libcxx/include/iosfwd libcxx/include/string libcxx/include/string_view
Index: libcxx/include/string_view =================================================================== --- libcxx/include/string_view +++ libcxx/include/string_view @@ -192,7 +192,26 @@ _LIBCPP_BEGIN_NAMESPACE_STD template<class _CharT, class _Traits = char_traits<_CharT> > -class _LIBCPP_TEMPLATE_VIS basic_string_view { + class _LIBCPP_TEMPLATE_VIS basic_string_view; + +typedef basic_string_view<char> string_view; +#ifndef _LIBCPP_NO_HAS_CHAR8_T +typedef basic_string_view<char8_t> u8string_view; +#endif +typedef basic_string_view<char16_t> u16string_view; +typedef basic_string_view<char32_t> u32string_view; +typedef basic_string_view<wchar_t> wstring_view; + +template<class _CharT, class _Traits> +class + _LIBCPP_PREFERRED_NAME(string_view) +#ifndef _LIBCPP_NO_HAS_CHAR8_T + _LIBCPP_PREFERRED_NAME(u8string_view) +#endif + _LIBCPP_PREFERRED_NAME(u16string_view) + _LIBCPP_PREFERRED_NAME(u32string_view) + _LIBCPP_PREFERRED_NAME(wstring_view) + basic_string_view { public: // types typedef _Traits traits_type; @@ -776,14 +795,6 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, basic_string_view<_CharT, _Traits> __str); -typedef basic_string_view<char> string_view; -#ifndef _LIBCPP_NO_HAS_CHAR8_T -typedef basic_string_view<char8_t> u8string_view; -#endif -typedef basic_string_view<char16_t> u16string_view; -typedef basic_string_view<char32_t> u32string_view; -typedef basic_string_view<wchar_t> wstring_view; - // [string.view.hash] template<class _CharT> struct _LIBCPP_TEMPLATE_VIS hash<basic_string_view<_CharT, char_traits<_CharT> > > Index: libcxx/include/string =================================================================== --- libcxx/include/string +++ libcxx/include/string @@ -665,8 +665,26 @@ #endif // _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT +#ifndef _LIBCPP_NO_HAS_CHAR8_T +typedef basic_string<char8_t> u8string; +#endif + +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS +typedef basic_string<char16_t> u16string; +typedef basic_string<char32_t> u32string; +#endif // _LIBCPP_HAS_NO_UNICODE_CHARS + template<class _CharT, class _Traits, class _Allocator> -class _LIBCPP_TEMPLATE_VIS basic_string +class + _LIBCPP_TEMPLATE_VIS +#ifndef _LIBCPP_NO_HAS_CHAR8_T + _LIBCPP_PREFERRED_NAME(u8string) +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + _LIBCPP_PREFERRED_NAME(u16string) + _LIBCPP_PREFERRED_NAME(u32string) +#endif + basic_string : private __basic_string_common<true> { public: @@ -4301,15 +4319,6 @@ __lhs.swap(__rhs); } -#ifndef _LIBCPP_NO_HAS_CHAR8_T -typedef basic_string<char8_t> u8string; -#endif - -#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS -typedef basic_string<char16_t> u16string; -typedef basic_string<char32_t> u32string; -#endif // _LIBCPP_HAS_NO_UNICODE_CHARS - _LIBCPP_FUNC_VIS int stoi (const string& __str, size_t* __idx = 0, int __base = 10); _LIBCPP_FUNC_VIS long stol (const string& __str, size_t* __idx = 0, int __base = 10); _LIBCPP_FUNC_VIS unsigned long stoul (const string& __str, size_t* __idx = 0, int __base = 10); Index: libcxx/include/iosfwd =================================================================== --- libcxx/include/iosfwd +++ libcxx/include/iosfwd @@ -185,6 +185,38 @@ typedef basic_ofstream<wchar_t> wofstream; typedef basic_fstream<wchar_t> wfstream; +#ifdef _LIBCPP_PREFERRED_NAME +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(ios) _LIBCPP_PREFERRED_NAME(wios) basic_ios; + +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(streambuf) _LIBCPP_PREFERRED_NAME(wstreambuf) basic_streambuf; +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(istream) _LIBCPP_PREFERRED_NAME(wistream) basic_istream; +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(ostream) _LIBCPP_PREFERRED_NAME(wostream) basic_ostream; +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(iostream) _LIBCPP_PREFERRED_NAME(wiostream) basic_iostream; + +template <class _CharT, class _Traits, class _Allocator> + class _LIBCPP_PREFERRED_NAME(stringbuf) _LIBCPP_PREFERRED_NAME(wstringbuf) basic_stringbuf; +template <class _CharT, class _Traits, class _Allocator> + class _LIBCPP_PREFERRED_NAME(istringstream) _LIBCPP_PREFERRED_NAME(wistringstream) basic_istringstream; +template <class _CharT, class _Traits, class _Allocator> + class _LIBCPP_PREFERRED_NAME(ostringstream) _LIBCPP_PREFERRED_NAME(wostringstream) basic_ostringstream; +template <class _CharT, class _Traits, class _Allocator> + class _LIBCPP_PREFERRED_NAME(stringstream) _LIBCPP_PREFERRED_NAME(wstringstream) basic_stringstream; + +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(filebuf) _LIBCPP_PREFERRED_NAME(wfilebuf) basic_filebuf; +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(ifstream) _LIBCPP_PREFERRED_NAME(wifstream) basic_ifstream; +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(ofstream) _LIBCPP_PREFERRED_NAME(wofstream) basic_ofstream; +template <class _CharT, class _Traits> + class _LIBCPP_PREFERRED_NAME(fstream) _LIBCPP_PREFERRED_NAME(wfstream) basic_fstream; +#endif + template <class _State> class _LIBCPP_TEMPLATE_VIS fpos; typedef fpos<mbstate_t> streampos; typedef fpos<mbstate_t> wstreampos; @@ -210,6 +242,10 @@ typedef basic_string<char, char_traits<char>, allocator<char> > string; typedef basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> > wstring; +#ifdef _LIBCPP_PREFERRED_NAME +template <class _CharT, class _Traits, class _Allocator> + class _LIBCPP_PREFERRED_NAME(string) _LIBCPP_PREFERRED_NAME(wstring) basic_string; +#endif // Include other forward declarations here template <class _Tp, class _Alloc = allocator<_Tp> > Index: libcxx/include/__config =================================================================== --- libcxx/include/__config +++ libcxx/include/__config @@ -1330,6 +1330,12 @@ #endif #endif // !defined(_LIBCPP_NODEBUG_TYPE) +#if __has_attribute(__preferred_name__) +#define _LIBCPP_PREFERRED_NAME(x) __attribute__((__preferred_name__(x))) +#else +#define _LIBCPP_PREFERRED_NAME(x) +#endif + #if defined(_LIBCPP_ABI_MICROSOFT) && \ (defined(_LIBCPP_COMPILER_MSVC) || __has_declspec_attribute(empty_bases)) # define _LIBCPP_DECLSPEC_EMPTY_BASES __declspec(empty_bases) Index: clang/test/SemaTemplate/attributes.cpp =================================================================== --- clang/test/SemaTemplate/attributes.cpp +++ clang/test/SemaTemplate/attributes.cpp @@ -63,3 +63,46 @@ // CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAR" template<typename T> [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations(); void UseAnnotations() { HasAnnotations<int>(); } + +namespace preferred_name { + int x [[clang::preferred_name("frank")]]; // expected-error {{expected a type}} + int y [[clang::preferred_name(int)]]; // expected-warning {{'preferred_name' attribute only applies to class templates}} + struct [[clang::preferred_name(int)]] A; // expected-warning {{'preferred_name' attribute only applies to class templates}} + 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'}} + template<typename T> struct C; + using X = C<int>; + typedef C<float> Y; + using Z = const C<double>; + 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'}} + template<typename T> struct [[clang::preferred_name(X), clang::preferred_name(Y)]] C; + template<typename T> struct [[clang::preferred_name(const X)]] C; // expected-error {{argument 'const preferred_name::X'}} + template<typename T> struct [[clang::preferred_name(Z)]] C; // expected-error {{argument 'preferred_name::Z' (aka 'const C<double>')}} + template<typename T> struct C {}; + + // CHECK: ClassTemplateDecl {{.*}} <line:[[@LINE-10]]:{{.*}} C + // CHECK: ClassTemplateSpecializationDecl {{.*}} struct C definition + // CHECK: TemplateArgument type 'int' + // CHECK-NOT: PreferredNameAttr + // CHECK: PreferredNameAttr {{.*}} preferred_name::X + // CHECK-NOT: PreferredNameAttr + // CHECK: CXXRecordDecl + // CHECK: ClassTemplateSpecializationDecl {{.*}} struct C definition + // CHECK: TemplateArgument type 'float' + // CHECK-NOT: PreferredNameAttr + // CHECK: PreferredNameAttr {{.*}} preferred_name::Y + // CHECK-NOT: PreferredNameAttr + // CHECK: CXXRecordDecl + // CHECK: ClassTemplateSpecializationDecl {{.*}} struct C definition + // CHECK: TemplateArgument type 'double' + // CHECK-NOT: PreferredNameAttr + // CHECK: CXXRecordDecl + + void f(C<int> a, C<float> b, C<double> c) { + auto p = a; + auto q = b; + auto r = c; + p.f(); // expected-error {{no member named 'f' in 'preferred_name::X'}} + q.f(); // expected-error {{no member named 'f' in 'preferred_name::Y'}} + r.f(); // expected-error {{no member named 'f' in 'preferred_name::C<double>'}} + } +} Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -548,12 +548,32 @@ S.addAMDGPUWavesPerEUAttr(New, Attr, MinExpr, MaxExpr); } +/// Determine whether the attribute A might be relevent to the declaration D. +/// If not, we can skip instantiating it. The attribute may or may not have +/// been instantiated yet. +static bool isRelevantAttr(const Decl *D, const Attr *A) { + // 'preferred_name' is only relevant to the matching specialization of the + // template. + if (auto *PNA = dyn_cast<PreferredNameAttr>(A)) { + QualType T = PNA->getTypedefType(); + auto *RD = cast<CXXRecordDecl>(D); + if (T->isDependentType() || RD->isDependentContext()) + return true; + return declaresSameEntity(T->getAsCXXRecordDecl(), RD); + } + + return true; +} + void Sema::InstantiateAttrsForDecl( const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New, LateInstantiatedAttrVec *LateAttrs, LocalInstantiationScope *OuterMostScope) { if (NamedDecl *ND = dyn_cast<NamedDecl>(New)) { for (const auto *TmplAttr : Tmpl->attrs()) { + if (!isRelevantAttr(New, TmplAttr)) + continue; + // FIXME: If any of the special case versions from InstantiateAttrs become // applicable to template declaration, we'll need to add them here. CXXThisScopeRAII ThisScope( @@ -562,7 +582,7 @@ Attr *NewAttr = sema::instantiateTemplateAttributeForDecl( TmplAttr, Context, *this, TemplateArgs); - if (NewAttr) + if (NewAttr && isRelevantAttr(New, NewAttr)) New->addAttr(NewAttr); } } @@ -692,6 +712,10 @@ continue; } + // Instantiated with the declaration. + if (isa<PreferredNameAttr>(TmplAttr)) + continue; + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -1380,6 +1380,34 @@ 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->getAs<TypedefType>() || T.hasQualifiers()) { + S.Diag(AL.getLoc(), diag::err_attribute_preferred_name_arg_invalid) + << T << CTD; + return; + } + + auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>( + T->getAsCXXRecordDecl()); + if (!CTSD || !declaresSameEntity(CTSD->getSpecializedTemplate(), CTD)) { + S.Diag(AL.getLoc(), diag::err_attribute_preferred_name_arg_invalid) + << T << CTD; + return; + } + + D->addAttr(::new (S.Context) PreferredNameAttr(S.Context, AL, TSI)); +} + 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 @@ -7704,6 +7732,9 @@ 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; Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -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,15 @@ } void TypePrinter::printRecordBefore(const RecordType *T, raw_ostream &OS) { + // Print the preferred name if we have one for this type. + for (PreferredNameAttr *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); } Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3937,6 +3937,9 @@ "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< Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -4387,6 +4387,30 @@ }]; } +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 declaration that refers to a +specialization of the class template. 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 = [{ Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -126,6 +126,9 @@ 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, @@ -2360,6 +2363,16 @@ let Documentation = [Undocumented]; } +def PreferredName : InheritableAttr { + let Spellings = [Clang<"preferred_name">]; + 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];
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits