Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -2132,3 +2132,9 @@ let SemaHandler = 0; let Documentation = [Undocumented]; } + +def InternalLinkage : InheritableAttr { + let Spellings = [GNU<"internal_linkage">, CXX11<"clang", "internal_linkage">]; + let Subjects = SubjectList<[Var, Function, CXXRecord]>; + let Documentation = [InternalLinkageDocs]; +} Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1666,6 +1666,15 @@ // not_tail_called on a virtual function is an error. [[clang::not_tail_called]] int foo2() override; }; + }]; +} +def InternalLinkageDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``internal_linkage`` attribute changes the linkage type of the declaration to internal. +This is similar to C-style ``static``, but can be used on classes and class methods. When applied to a class definition, +this attribute affects all methods and static data members of that class. +This can be used to contain the ABI of a C++ library by excluding unwanted class methods from the export tables. }]; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4085,6 +4085,12 @@ InGroup>; def note_used_here : Note<"used here">; +def err_internal_linkage_redeclaration : Error< + "'internal_linkage' attribute does not appear on the first declaration of %0">; +def warn_internal_linkage_local_storage : Warning< + "'internal_linkage' attribute on a non-static local variable is ignored">, + InGroup; + def ext_internal_in_extern_inline : ExtWarn< "static %select{function|variable}0 %1 is used in an inline function with " "external linkage">, InGroup; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2115,6 +2115,11 @@ unsigned AttrSpellingListIndex); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex); + InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, SourceRange Range, + IdentifierInfo *Ident, + unsigned AttrSpellingListIndex); + CommonAttr *mergeCommonAttr(Decl *D, SourceRange Range, IdentifierInfo *Ident, + unsigned AttrSpellingListIndex); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1217,6 +1217,10 @@ static LinkageInfo computeLVForDecl(const NamedDecl *D, LVComputationKind computation) { + // Internal_linkage attribute overrides other considerations. + if (D->hasAttr()) + return LinkageInfo::internal(); + // Objective-C: treat all Objective-C declarations as having external // linkage. switch (D->getKind()) { @@ -1307,6 +1311,10 @@ public: static LinkageInfo getLVForDecl(const NamedDecl *D, LVComputationKind computation) { + // Internal_linkage attribute overrides other considerations. + if (D->hasAttr()) + return LinkageInfo::internal(); + if (computation == LVForLinkageOnly && D->hasCachedLinkage()) return LinkageInfo(D->getCachedLinkage(), DefaultVisibility, false); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2216,6 +2216,15 @@ NewAttr = S.mergeMinSizeAttr(D, MA->getRange(), AttrSpellingListIndex); else if (const auto *OA = dyn_cast(Attr)) NewAttr = S.mergeOptimizeNoneAttr(D, OA->getRange(), AttrSpellingListIndex); + else if (const auto *InternalLinkageA = dyn_cast(Attr)) + NewAttr = S.mergeInternalLinkageAttr( + D, InternalLinkageA->getRange(), + &S.Context.Idents.get(InternalLinkageA->getSpelling()), + AttrSpellingListIndex); + else if (const auto *CommonA = dyn_cast(Attr)) + NewAttr = S.mergeCommonAttr(D, CommonA->getRange(), + &S.Context.Idents.get(CommonA->getSpelling()), + AttrSpellingListIndex); else if (isa(Attr)) // AlignedAttrs are handled separately, because we need to handle all // such attributes on a declaration at the same time. @@ -2664,6 +2673,13 @@ } } + if (New->hasAttr() && + !Old->hasAttr()) { + Diag(New->getLocation(), diag::err_internal_linkage_redeclaration) + << New->getDeclName(); + Diag(Old->getLocation(), diag::note_previous_definition); + New->dropAttr(); + } // If a function is first declared with a calling convention, but is later // declared or defined without one, all following decls assume the calling @@ -3377,6 +3393,14 @@ New->dropAttr(); } + if (New->hasAttr() && + !Old->hasAttr()) { + Diag(New->getLocation(), diag::err_internal_linkage_redeclaration) + << New->getDeclName(); + Diag(Old->getLocation(), diag::note_previous_definition); + New->dropAttr(); + } + // Merge the types. VarDecl *MostRecent = Old->getMostRecentDecl(); if (MostRecent != Old) { Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -244,11 +244,12 @@ /// \brief Diagnose mutually exclusive attributes when present on a given /// declaration. Returns true if diagnosed. template -static bool checkAttrMutualExclusion(Sema &S, Decl *D, - const AttributeList &Attr) { +static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range, + IdentifierInfo *Ident) { if (AttrTy *A = D->getAttr()) { - S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible) - << Attr.getName() << A; + S.Diag(Range.getBegin(), diag::err_attributes_are_not_compatible) << Ident + << A; + S.Diag(A->getLocation(), diag::note_conflicting_attribute); return true; } return false; @@ -1523,7 +1524,7 @@ } static void handleColdAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion(S, D, Attr)) + if (checkAttrMutualExclusion(S, D, Attr.getRange(), Attr.getName())) return; D->addAttr(::new (S.Context) ColdAttr(Attr.getRange(), S.Context, @@ -1531,7 +1532,7 @@ } static void handleHotAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion(S, D, Attr)) + if (checkAttrMutualExclusion(S, D, Attr.getRange(), Attr.getName())) return; D->addAttr(::new (S.Context) HotAttr(Attr.getRange(), S.Context, @@ -1573,12 +1574,13 @@ static void handleCommonAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.CPlusPlus) { S.Diag(Attr.getLoc(), diag::err_attribute_not_supported_in_lang) - << Attr.getName() << AttributeLangSupport::Cpp; + << Attr.getName() << AttributeLangSupport::Cpp; return; } - D->addAttr(::new (S.Context) CommonAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + if (CommonAttr *CA = S.mergeCommonAttr(D, Attr.getRange(), Attr.getName(), + Attr.getAttributeSpellingListIndex())) + D->addAttr(CA); } static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { @@ -1703,7 +1705,8 @@ static void handleNotTailCalledAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion(S, D, Attr)) + if (checkAttrMutualExclusion(S, D, Attr.getRange(), + Attr.getName())) return; D->addAttr(::new (S.Context) NotTailCalledAttr( @@ -3392,6 +3395,42 @@ AttrSpellingListIndex); } +CommonAttr *Sema::mergeCommonAttr(Decl *D, SourceRange Range, + IdentifierInfo *Ident, + unsigned AttrSpellingListIndex) { + if (checkAttrMutualExclusion(*this, D, Range, Ident)) + return nullptr; + + return ::new (Context) CommonAttr(Range, Context, AttrSpellingListIndex); +} + +InternalLinkageAttr * +Sema::mergeInternalLinkageAttr(Decl *D, SourceRange Range, + IdentifierInfo *Ident, + unsigned AttrSpellingListIndex) { + if (auto VD = dyn_cast(D)) { + // Attribute applies to Var but not any subclass of it (like ParmVar, + // ImplicitParm or VarTemplateSpecialization). + if (VD->getKind() != Decl::Var) { + Diag(Range.getBegin(), diag::warn_attribute_wrong_decl_type) + << Ident << (getLangOpts().CPlusPlus ? ExpectedFunctionVariableOrClass + : ExpectedVariableOrFunction); + return nullptr; + } + // Attribute does not apply to non-static local variables. + if (VD->hasLocalStorage()) { + Diag(VD->getLocation(), diag::warn_internal_linkage_local_storage); + return nullptr; + } + } + + if (checkAttrMutualExclusion(*this, D, Range, Ident)) + return nullptr; + + return ::new (Context) + InternalLinkageAttr(Range, Context, AttrSpellingListIndex); +} + MinSizeAttr *Sema::mergeMinSizeAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex) { if (OptimizeNoneAttr *Optnone = D->getAttr()) { @@ -3428,7 +3467,8 @@ static void handleAlwaysInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion(S, D, Attr)) + if (checkAttrMutualExclusion(S, D, Attr.getRange(), + Attr.getName())) return; if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr( @@ -4014,7 +4054,8 @@ static void handleCFAuditedTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion(S, D, Attr)) + if (checkAttrMutualExclusion(S, D, Attr.getRange(), + Attr.getName())) return; D->addAttr(::new (S.Context) @@ -4024,7 +4065,8 @@ static void handleCFUnknownTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion(S, D, Attr)) + if (checkAttrMutualExclusion(S, D, Attr.getRange(), + Attr.getName())) return; D->addAttr(::new (S.Context) @@ -4646,6 +4688,14 @@ Attr.getAttributeSpellingListIndex())); } +static void handleInternalLinkageAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (InternalLinkageAttr *Internal = + S.mergeInternalLinkageAttr(D, Attr.getRange(), Attr.getName(), + Attr.getAttributeSpellingListIndex())) + D->addAttr(Internal); +} + /// Handles semantic checking for features that are common to all attributes, /// such as checking whether a parameter was properly specified, or the correct /// number of arguments were passed, etc. @@ -5090,6 +5140,9 @@ case AttributeList::AT_OpenCLImageAccess: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_InternalLinkage: + handleInternalLinkageAttr(S, D, Attr); + break; // Microsoft attributes: case AttributeList::AT_MSNoVTable: Index: test/CodeGenCXX/attribute_internal_linkage.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/attribute_internal_linkage.cpp @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++11 -emit-llvm -o - %s | FileCheck %s + +__attribute__((internal_linkage)) void f() {} +// CHECK-DAG: define internal void @_ZL1fv + +class A { +public: + static int y __attribute__((internal_linkage)); + static int y2 [[clang::internal_linkage]]; +// CHECK-DAG: @_ZN1A1yE = internal global +// CHECK-DAG: @_ZN1A2y2E = internal global + void f1() __attribute__((internal_linkage)); +// CHECK-DAG: define internal void @_ZN1A2f1Ev + void f2() __attribute__((internal_linkage)) {} +// CHECK-DAG: define internal void @_ZN1A2f2Ev + static void f4() __attribute__((internal_linkage)) {} +// CHECK-DAG: define internal void @_ZN1A2f4Ev + A() __attribute__((internal_linkage)) {} +// CHECK-DAG: define internal void @_ZN1AC1Ev +// CHECK-DAG: define internal void @_ZN1AC2Ev + ~A() __attribute__((internal_linkage)) {} +// CHECK-DAG: define internal void @_ZN1AD1Ev +// CHECK-DAG: define internal void @_ZN1AD2Ev +}; + +int A::y; +int A::y2; + +void A::f1() { +} + +// Forward declaration w/o an attribute. +class B; + +// Internal_linkage on a class affects all its members. +class __attribute__((internal_linkage)) B { +public: + B() {} + // CHECK-DAG: define internal void @_ZNL1BC1Ev + // CHECK-DAG: define internal void @_ZNL1BC2Ev + ~B() {} + // CHECK-DAG: define internal void @_ZNL1BD1Ev + // CHECK-DAG: define internal void @_ZNL1BD2Ev + void f() {}; + // CHECK-DAG: define internal void @_ZNL1B1fEv + static int x; + // CHECK-DAG: @_ZNL1B1xE = internal global +}; + +int B::x; + +// Forward declaration with the attribute. +class __attribute__((internal_linkage)) C; +class C { +public: + static int x; + // CHECK-DAG: @_ZNL1C1xE = internal global +}; + +int C::x; + +__attribute__((internal_linkage)) void g(); +void g() {} +// CHECK-DAG: define internal void @_ZL1gv() + +void use() { + A a; + a.f1(); + a.f2(); + A::f4(); + f(); + int &Y = A::y; + int &Y2 = A::y2; + B b; + b.f(); + int &XX2 = B::x; + g(); + int &XX3 = C::x; +} Index: test/Sema/attr-coldhot.c =================================================================== --- test/Sema/attr-coldhot.c +++ test/Sema/attr-coldhot.c @@ -6,5 +6,7 @@ int var1 __attribute__((__cold__)); // expected-warning{{'__cold__' attribute only applies to functions}} int var2 __attribute__((__hot__)); // expected-warning{{'__hot__' attribute only applies to functions}} -int qux() __attribute__((__hot__)) __attribute__((__cold__)); // expected-error{{'__hot__' and 'cold' attributes are not compatible}} -int baz() __attribute__((__cold__)) __attribute__((__hot__)); // expected-error{{'__cold__' and 'hot' attributes are not compatible}} +int qux() __attribute__((__hot__)) __attribute__((__cold__)); // expected-error{{'__hot__' and 'cold' attributes are not compatible}} \ +// expected-note{{conflicting attribute is here}} +int baz() __attribute__((__cold__)) __attribute__((__hot__)); // expected-error{{'__cold__' and 'hot' attributes are not compatible}} \ +// expected-note{{conflicting attribute is here}} Index: test/Sema/attr-notail.c =================================================================== --- test/Sema/attr-notail.c +++ test/Sema/attr-notail.c @@ -1,7 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -int callee0() __attribute__((not_tail_called,always_inline)); // expected-error{{'not_tail_called' and 'always_inline' attributes are not compatible}} -int callee1() __attribute__((always_inline,not_tail_called)); // expected-error{{'always_inline' and 'not_tail_called' attributes are not compatible}} +int callee0() __attribute__((not_tail_called,always_inline)); // expected-error{{'not_tail_called' and 'always_inline' attributes are not compatible}} \ +// expected-note{{conflicting attribute is here}} +int callee1() __attribute__((always_inline,not_tail_called)); // expected-error{{'always_inline' and 'not_tail_called' attributes are not compatible}} \ +// expected-note{{conflicting attribute is here}} int foo(int a) { return a ? callee0() : callee1(); Index: test/Sema/internal_linkage.c =================================================================== --- /dev/null +++ test/Sema/internal_linkage.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int var __attribute__((internal_linkage)); +int var2 __attribute__((internal_linkage,common)); // expected-error{{'internal_linkage' and 'common' attributes are not compatible}} \ + // expected-note{{conflicting attribute is here}} +int var3 __attribute__((common,internal_linkage)); // expected-error{{'common' and 'internal_linkage' attributes are not compatible}} \ + // expected-note{{conflicting attribute is here}} + +int var4 __attribute__((common)); // expected-error{{'common' and 'internal_linkage' attributes are not compatible}} \ +// expected-note{{previous definition is here}} +int var4 __attribute__((internal_linkage)); // expected-note{{conflicting attribute is here}} \ +// expected-error{{'internal_linkage' attribute does not appear on the first declaration of 'var4'}} + +int var5 __attribute__((internal_linkage)); // expected-error{{'internal_linkage' and 'common' attributes are not compatible}} +int var5 __attribute__((common)); // expected-note{{conflicting attribute is here}} + +__attribute__((internal_linkage)) int f() {} +struct __attribute__((internal_linkage)) S { // expected-warning{{'internal_linkage' attribute only applies to variables and functions}} +}; + +__attribute__((internal_linkage("foo"))) int g() {} // expected-error{{'internal_linkage' attribute takes no arguments}} Index: test/SemaCXX/internal_linkage.cpp =================================================================== --- /dev/null +++ test/SemaCXX/internal_linkage.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +int f() __attribute__((internal_linkage)); + +class A; +class __attribute__((internal_linkage)) A { +public: + int x __attribute__((internal_linkage)); // expected-warning{{'internal_linkage' attribute only applies to variables, functions and classes}} + static int y __attribute__((internal_linkage)); + void f1() __attribute__((internal_linkage)); + void f2() __attribute__((internal_linkage)) {} + static void f3() __attribute__((internal_linkage)) {} + void f4(); // expected-note{{previous definition is here}} + static int zz; // expected-note{{previous definition is here}} + A() __attribute__((internal_linkage)) {} + ~A() __attribute__((internal_linkage)) {} + A& operator=(const A&) __attribute__((internal_linkage)) { return *this; } + struct { + int z __attribute__((internal_linkage)); // expected-warning{{'internal_linkage' attribute only applies to variables, functions and classes}} + }; +}; + +__attribute__((internal_linkage)) void A::f4() {} // expected-error{{'internal_linkage' attribute does not appear on the first declaration of 'f4'}} + +__attribute__((internal_linkage)) int A::zz; // expected-error{{'internal_linkage' attribute does not appear on the first declaration of 'zz'}} + +namespace Z __attribute__((internal_linkage)) { // expected-warning{{'internal_linkage' attribute only applies to variables, functions and classes}} +} + +__attribute__((internal_linkage("foo"))) int g() {} // expected-error{{'internal_linkage' attribute takes no arguments}} + +[[clang::internal_linkage]] int h() {} + +enum struct __attribute__((internal_linkage)) E { // expected-warning{{'internal_linkage' attribute only applies to variables, functions and classes}} + a = 1, + b = 2 +}; + +int A::y; + +void A::f1() { +} + +void g(int a [[clang::internal_linkage]]) { // expected-warning{{'internal_linkage' attribute only applies to variables, functions and classes}} + int x [[clang::internal_linkage]]; // expected-warning{{'internal_linkage' attribute on a non-static local variable is ignored}} + static int y [[clang::internal_linkage]]; +}