Index: cfe/trunk/docs/ItaniumMangleAbiTags.rst =================================================================== --- cfe/trunk/docs/ItaniumMangleAbiTags.rst +++ cfe/trunk/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. Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -358,6 +358,14 @@ // 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">]; Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -2132,3 +2132,16 @@ 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. + }]; +} Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -2477,7 +2477,8 @@ "Objective-C instance methods|init methods of interface or class extension declarations|" "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}1">, + "interface or protocol declarations|kernel functions|non-K&R-style functions|" + "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_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'">; Index: cfe/trunk/include/clang/Sema/AttributeList.h =================================================================== --- cfe/trunk/include/clang/Sema/AttributeList.h +++ cfe/trunk/include/clang/Sema/AttributeList.h @@ -895,7 +895,8 @@ ExpectedStructOrTypedef, ExpectedObjectiveCInterfaceOrProtocol, ExpectedKernelFunction, - ExpectedFunctionWithProtoType + ExpectedFunctionWithProtoType, + ExpectedStructClassVariableFunctionOrInlineNamespace }; } // end namespace clang Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -2398,6 +2398,24 @@ } } + // 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; Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -4615,6 +4615,42 @@ 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 @@ 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: Index: cfe/trunk/test/SemaCXX/attr-abi-tag-syntax.cpp =================================================================== --- cfe/trunk/test/SemaCXX/attr-abi-tag-syntax.cpp +++ cfe/trunk/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}}