From 707118c24cbb8849f27204b27dddfe8213a3dbb5 Mon Sep 17 00:00:00 2001 From: Dmitry Polukhin Date: Wed, 9 Mar 2016 15:30:53 +0000 Subject: [PATCH] [GCC] PR23529 Sema part of attrbute abi_tag support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Original patch by Stefan Bühler http://reviews.llvm.org/D12834 Difference between original and this one: - fixed all comments in original code review - added more tests, all new diagnostics now covered by tests - moved abi_tag on re-declaration checks to Sema::mergeDeclAttributes where they actually may work as designed - clang-format + other stylistic changes Mangle part will be sent for review as a separate patch. Differential Revision: http://reviews.llvm.org/D17567 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263015 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ItaniumMangleAbiTags.rst | 101 +++++++++++++++++++++ include/clang/Basic/Attr.td | 8 ++ include/clang/Basic/AttrDocs.td | 13 +++ include/clang/Basic/DiagnosticSemaKinds.td | 10 +- include/clang/Sema/AttributeList.h | 3 +- lib/Sema/SemaDecl.cpp | 18 ++++ lib/Sema/SemaDeclAttr.cpp | 39 ++++++++ test/SemaCXX/attr-abi-tag-syntax.cpp | 43 +++++++++ 8 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 docs/ItaniumMangleAbiTags.rst create mode 100644 test/SemaCXX/attr-abi-tag-syntax.cpp diff --git a/docs/ItaniumMangleAbiTags.rst b/docs/ItaniumMangleAbiTags.rst new file mode 100644 index 0000000000..8de20931bb --- /dev/null +++ b/docs/ItaniumMangleAbiTags.rst @@ -0,0 +1,101 @@ +======== +ABI tags +======== + +Introduction +============ + +This text tries to describe gcc semantic for mangling "abi_tag" attributes +described in https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html + +There is no guarantee the following rules are correct, complete or make sense +in any way as they were determined empirically by experiments with gcc5. + +Declaration +=========== + +ABI tags are declared in an abi_tag attribute and can be applied to a +function, variable, class or inline namespace declaration. The attribute takes +one or more strings (called tags); the order does not matter. + +See https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html for +details. + +Tags on an inline namespace are called "implicit tags", all other tags are +"explicit tags". + +Mangling +======== + +All tags that are "active" on an are emitted after the +, before or , and are part of +the same the is. + +They are mangled as: + + ::= * # sort by name + ::= B + +Example: + + __attribute__((abi_tag("test"))) + void Func(); + + gets mangled as: _Z4FuncB4testv (prettified as `Func[abi:test]()`) + +Active tags +=========== + +A namespace does not have any active tags. For types (class / struct / union / +enum), the explicit tags are the active tags. + +For variables and functions, the active tags are the explicit tags plus any +"required tags" which are not in the "available tags" set: + + derived-tags := (required-tags - available-tags) + active-tags := explicit-tags + derived-tags + +Required tags for a function +============================ + +If a function is used as a local scope for another name, and is part of +another function as local scope, it doesn't have any required tags. + +If a function is used as a local scope for a guard variable name, it doesn't +have any required tags. + +Otherwise the function requires any implicit or explicit tag used in the name +for the return type. + +Example: + namespace A { + inline namespace B __attribute__((abi_tag)) { + struct C { int x; }; + } + } + + A::C foo(); + + gets mangled as: _Z3fooB1Bv (prettified as `foo[abi:B]()`) + +Required tags for a variable +============================ + +A variable requires any implicit or explicit tag used in its type. + +Available tags +============== + +All tags used in the prefix and in the template arguments for a name are +available. Also, for functions, all tags from the +(which might include the return type for template functions) are available. + +For s all active tags used in the local part () are available, but not implicit tags which were not active. + +Implicit and explicit tags used in the for a function (as +in the type of a cast operator) are NOT available. + +Example: a cast operator to std::string (which is +std::__cxx11::basic_string<...>) will use 'cxx11' as an active tag, as it is +required from the return type `std::string` but not available. diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 496e395197..21078e0b25 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -358,6 +358,14 @@ class IgnoredAttr : Attr { // Attributes begin here // +def AbiTag : Attr { + let Spellings = [GCC<"abi_tag">]; + let Args = [VariadicStringArgument<"Tags">]; + let Subjects = SubjectList<[Struct, Var, Function, Namespace], ErrorDiag, + "ExpectedStructClassVariableFunctionOrInlineNamespace">; + let Documentation = [AbiTagsDocs]; +} + def AddressSpace : TypeAttr { let Spellings = [GNU<"address_space">]; let Args = [IntArgument<"AddressSpace">]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 97cb9b2c20..2bf8a82bee 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -2132,3 +2132,16 @@ directly construct objects into them without relying on language optimizations like C++'s named return value optimization (NRVO). }]; } + +def AbiTagsDocs : Documentation { + let Content = [{ +The ``abi_tag`` attribute can be applied to a function, variable, class or +inline namespace declaration to modify the mangled name of the entity. It gives +the ability to distinguish between different versions of the same entity but +with different ABI versions supported. For example, a newer version of a class +could have a different set of data members and thus have a different size. Using +the ``abi_tag`` attribute, it is possible to have different mangled names for +a global variable of the class type. Therefor, the old code could keep using +the old manged name and the new code will use the new mangled name with tags. + }]; +} diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9c3ebcadbc..e547302055 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2477,7 +2477,8 @@ def warn_attribute_wrong_decl_type : Warning< "variables, functions and classes|Objective-C protocols|" "functions and global variables|structs, unions, and typedefs|structs and typedefs|" "interface or protocol declarations|kernel functions|non-K&R-style functions|" - "variables, enums, fields and typedefs|functions, methods, enums, and classes}1">, + "variables, enums, fields and typedefs|functions, methods, enums, and classes|" + "structs, classes, variables, functions, and inline namespaces}1">, InGroup; def err_attribute_wrong_decl_type : Error; def warn_type_attribute_wrong_type : Warning< @@ -4195,6 +4196,13 @@ def err_definition_of_explicitly_defaulted_member : Error< def err_redefinition_extern_inline : Error< "redefinition of a 'extern inline' function %0 is not supported in " "%select{C99 mode|C++}1">; +def warn_attr_abi_tag_namespace : Warning< + "'abi_tag' attribute on %select{non-inline|anonymous}0 namespace ignored">, + InGroup; +def err_abi_tag_on_redeclaration : Error< + "cannot add 'abi_tag' attribute in a redeclaration">; +def err_new_abi_tag_on_redeclaration : Error< + "'abi_tag' %0 missing in original declaration">; def note_deleted_dtor_no_operator_delete : Note< "virtual destructor requires an unambiguous, accessible 'operator delete'">; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 8a0fbf23fe..a3e656e6c0 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -895,7 +895,8 @@ enum AttributeDeclKind { ExpectedKernelFunction, ExpectedFunctionWithProtoType, ExpectedVariableEnumFieldOrTypedef, - ExpectedFunctionMethodEnumOrClass + ExpectedFunctionMethodEnumOrClass, + ExpectedStructClassVariableFunctionOrInlineNamespace }; } // end namespace clang diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index db741bc6e3..0c3c4399f6 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2398,6 +2398,24 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, } } + // Re-declaration cannot add abi_tag's. + if (const auto *NewAbiTagAttr = New->getAttr()) { + if (const auto *OldAbiTagAttr = Old->getAttr()) { + for (const auto &NewTag : NewAbiTagAttr->tags()) { + if (std::find(OldAbiTagAttr->tags_begin(), OldAbiTagAttr->tags_end(), + NewTag) == OldAbiTagAttr->tags_end()) { + Diag(NewAbiTagAttr->getLocation(), + diag::err_new_abi_tag_on_redeclaration) + << NewTag; + Diag(OldAbiTagAttr->getLocation(), diag::note_previous_declaration); + } + } + } else { + Diag(NewAbiTagAttr->getLocation(), diag::err_abi_tag_on_redeclaration); + Diag(Old->getLocation(), diag::note_previous_declaration); + } + } + if (!Old->hasAttrs()) return; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 1fca27f8b9..f8cec75204 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4615,6 +4615,42 @@ static void handleDeclspecThreadAttr(Sema &S, Decl *D, Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleAbiTagAttr(Sema &S, Decl *D, const AttributeList &Attr) { + SmallVector Tags; + for (unsigned I = 0, E = Attr.getNumArgs(); I != E; ++I) { + StringRef Tag; + if (!S.checkStringLiteralArgumentAttr(Attr, I, Tag)) + return; + Tags.push_back(Tag); + } + + if (const auto *NS = dyn_cast(D)) { + if (!NS->isInline()) { + S.Diag(Attr.getLoc(), diag::warn_attr_abi_tag_namespace) << 0; + return; + } + if (NS->isAnonymousNamespace()) { + S.Diag(Attr.getLoc(), diag::warn_attr_abi_tag_namespace) << 1; + return; + } + if (Attr.getNumArgs() == 0) + Tags.push_back(NS->getName()); + } else if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + + // Store tags sorted and without duplicates. + std::sort(Tags.begin(), Tags.end()); + Tags.erase(std::unique(Tags.begin(), Tags.end()), Tags.end()); + + D->addAttr(::new (S.Context) + AbiTagAttr(Attr.getRange(), S.Context, Tags.data(), Tags.size(), + Attr.getAttributeSpellingListIndex())); + + // FIXME: remove this warning as soon as mangled part is ready. + S.Diag(Attr.getRange().getBegin(), diag::warn_attribute_ignored) + << Attr.getName(); +} + static void handleARMInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. @@ -5637,6 +5673,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_Thread: handleDeclspecThreadAttr(S, D, Attr); break; + case AttributeList::AT_AbiTag: + handleAbiTagAttr(S, D, Attr); + break; // Thread safety attributes: case AttributeList::AT_AssertExclusiveLock: diff --git a/test/SemaCXX/attr-abi-tag-syntax.cpp b/test/SemaCXX/attr-abi-tag-syntax.cpp new file mode 100644 index 0000000000..4a4534a52f --- /dev/null +++ b/test/SemaCXX/attr-abi-tag-syntax.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +namespace N1 { + +namespace __attribute__((__abi_tag__)) {} +// expected-warning@-1 {{'abi_tag' attribute on non-inline namespace ignored}} + +namespace N __attribute__((__abi_tag__)) {} +// expected-warning@-1 {{'abi_tag' attribute on non-inline namespace ignored}} + +} // namespace N1 + +namespace N2 { + +inline namespace __attribute__((__abi_tag__)) {} +// expected-warning@-1 {{'abi_tag' attribute on anonymous namespace ignored}} + +inline namespace N __attribute__((__abi_tag__)) {} +// FIXME: remove this warning as soon as attribute fully supported. +// expected-warning@-2 {{'__abi_tag__' attribute ignored}} + +} // namespcace N2 + +__attribute__((abi_tag("B", "A"))) extern int a1; +// FIXME: remove this warning as soon as attribute fully supported. +// expected-warning@-2 {{'abi_tag' attribute ignored}} + +__attribute__((abi_tag("A", "B"))) extern int a1; +// expected-note@-1 {{previous declaration is here}} +// FIXME: remove this warning as soon as attribute fully supported. +// expected-warning@-3 {{'abi_tag' attribute ignored}} + +__attribute__((abi_tag("A", "C"))) extern int a1; +// expected-error@-1 {{'abi_tag' C missing in original declaration}} +// FIXME: remove this warning as soon as attribute fully supported. +// expected-warning@-3 {{'abi_tag' attribute ignored}} + +extern int a2; +// expected-note@-1 {{previous declaration is here}} +__attribute__((abi_tag("A")))extern int a2; +// expected-error@-1 {{cannot add 'abi_tag' attribute in a redeclaration}} +// FIXME: remove this warning as soon as attribute fully supported. +// expected-warning@-3 {{'abi_tag' attribute ignored}} -- 2.40.0