Skip to content

Commit

Permalink
Implement __attribute__((internal_linkage)).
Browse files Browse the repository at this point in the history
The attrubite is applicable to functions and variables and changes
the linkage of the subject to internal.

This is the same functionality as C-style "static", but applicable to
class methods; and the same as anonymouns namespaces, but can apply
to individual methods of a class.

Following the proposal in
http://lists.llvm.org/pipermail/cfe-dev/2015-October/045580.html

llvm-svn: 252648
  • Loading branch information
eugenis committed Nov 10, 2015
1 parent 2d5fb8c commit ae6ebd3
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 17 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2132,3 +2132,9 @@ def OMPThreadPrivateDecl : InheritableAttr {
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];
}
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,15 @@ Marking virtual functions as ``not_tail_called`` is an error:
// 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.
}];
}
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -4085,6 +4085,12 @@ def warn_undefined_inline : Warning<"inline function %q0 is not defined">,
InGroup<DiagGroup<"undefined-inline">>;
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<IgnoredAttributes>;

def ext_internal_in_extern_inline : ExtWarn<
"static %select{function|variable}0 %1 is used in an inline function with "
"external linkage">, InGroup<StaticInInline>;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2115,6 +2115,11 @@ class Sema {
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);
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,10 @@ getOutermostEnclosingLambda(const CXXRecordDecl *Record) {

static LinkageInfo computeLVForDecl(const NamedDecl *D,
LVComputationKind computation) {
// Internal_linkage attribute overrides other considerations.
if (D->hasAttr<InternalLinkageAttr>())
return LinkageInfo::internal();

// Objective-C: treat all Objective-C declarations as having external
// linkage.
switch (D->getKind()) {
Expand Down Expand Up @@ -1307,6 +1311,10 @@ class LinkageComputer {
public:
static LinkageInfo getLVForDecl(const NamedDecl *D,
LVComputationKind computation) {
// Internal_linkage attribute overrides other considerations.
if (D->hasAttr<InternalLinkageAttr>())
return LinkageInfo::internal();

if (computation == LVForLinkageOnly && D->hasCachedLinkage())
return LinkageInfo(D->getCachedLinkage(), DefaultVisibility, false);

Expand Down
24 changes: 24 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,15 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
NewAttr = S.mergeMinSizeAttr(D, MA->getRange(), AttrSpellingListIndex);
else if (const auto *OA = dyn_cast<OptimizeNoneAttr>(Attr))
NewAttr = S.mergeOptimizeNoneAttr(D, OA->getRange(), AttrSpellingListIndex);
else if (const auto *InternalLinkageA = dyn_cast<InternalLinkageAttr>(Attr))
NewAttr = S.mergeInternalLinkageAttr(
D, InternalLinkageA->getRange(),
&S.Context.Idents.get(InternalLinkageA->getSpelling()),
AttrSpellingListIndex);
else if (const auto *CommonA = dyn_cast<CommonAttr>(Attr))
NewAttr = S.mergeCommonAttr(D, CommonA->getRange(),
&S.Context.Idents.get(CommonA->getSpelling()),
AttrSpellingListIndex);
else if (isa<AlignedAttr>(Attr))
// AlignedAttrs are handled separately, because we need to handle all
// such attributes on a declaration at the same time.
Expand Down Expand Up @@ -2664,6 +2673,13 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD,
}
}

if (New->hasAttr<InternalLinkageAttr>() &&
!Old->hasAttr<InternalLinkageAttr>()) {
Diag(New->getLocation(), diag::err_internal_linkage_redeclaration)
<< New->getDeclName();
Diag(Old->getLocation(), diag::note_previous_definition);
New->dropAttr<InternalLinkageAttr>();
}

// If a function is first declared with a calling convention, but is later
// declared or defined without one, all following decls assume the calling
Expand Down Expand Up @@ -3377,6 +3393,14 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
New->dropAttr<WeakImportAttr>();
}

if (New->hasAttr<InternalLinkageAttr>() &&
!Old->hasAttr<InternalLinkageAttr>()) {
Diag(New->getLocation(), diag::err_internal_linkage_redeclaration)
<< New->getDeclName();
Diag(Old->getLocation(), diag::note_previous_definition);
New->dropAttr<InternalLinkageAttr>();
}

// Merge the types.
VarDecl *MostRecent = Old->getMostRecentDecl();
if (MostRecent != Old) {
Expand Down
79 changes: 66 additions & 13 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,12 @@ static bool checkUInt32Argument(Sema &S, const AttributeList &Attr,
/// \brief Diagnose mutually exclusive attributes when present on a given
/// declaration. Returns true if diagnosed.
template <typename AttrTy>
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<AttrTy>()) {
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;
Expand Down Expand Up @@ -1523,15 +1524,15 @@ static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}

static void handleColdAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (checkAttrMutualExclusion<HotAttr>(S, D, Attr))
if (checkAttrMutualExclusion<HotAttr>(S, D, Attr.getRange(), Attr.getName()))
return;

D->addAttr(::new (S.Context) ColdAttr(Attr.getRange(), S.Context,
Attr.getAttributeSpellingListIndex()));
}

static void handleHotAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (checkAttrMutualExclusion<ColdAttr>(S, D, Attr))
if (checkAttrMutualExclusion<ColdAttr>(S, D, Attr.getRange(), Attr.getName()))
return;

D->addAttr(::new (S.Context) HotAttr(Attr.getRange(), S.Context,
Expand Down Expand Up @@ -1573,12 +1574,13 @@ static void handleRestrictAttr(Sema &S, Decl *D, const AttributeList &Attr) {
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) {
Expand Down Expand Up @@ -1703,7 +1705,8 @@ static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D,

static void handleNotTailCalledAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<AlwaysInlineAttr>(S, D, Attr))
if (checkAttrMutualExclusion<AlwaysInlineAttr>(S, D, Attr.getRange(),
Attr.getName()))
return;

D->addAttr(::new (S.Context) NotTailCalledAttr(
Expand Down Expand Up @@ -3392,6 +3395,42 @@ AlwaysInlineAttr *Sema::mergeAlwaysInlineAttr(Decl *D, SourceRange Range,
AttrSpellingListIndex);
}

CommonAttr *Sema::mergeCommonAttr(Decl *D, SourceRange Range,
IdentifierInfo *Ident,
unsigned AttrSpellingListIndex) {
if (checkAttrMutualExclusion<InternalLinkageAttr>(*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<VarDecl>(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<CommonAttr>(*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<OptimizeNoneAttr>()) {
Expand Down Expand Up @@ -3428,7 +3467,8 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, SourceRange Range,

static void handleAlwaysInlineAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<NotTailCalledAttr>(S, D, Attr))
if (checkAttrMutualExclusion<NotTailCalledAttr>(S, D, Attr.getRange(),
Attr.getName()))
return;

if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr(
Expand Down Expand Up @@ -4014,7 +4054,8 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D,

static void handleCFAuditedTransferAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<CFUnknownTransferAttr>(S, D, Attr))
if (checkAttrMutualExclusion<CFUnknownTransferAttr>(S, D, Attr.getRange(),
Attr.getName()))
return;

D->addAttr(::new (S.Context)
Expand All @@ -4024,7 +4065,8 @@ static void handleCFAuditedTransferAttr(Sema &S, Decl *D,

static void handleCFUnknownTransferAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<CFAuditedTransferAttr>(S, D, Attr))
if (checkAttrMutualExclusion<CFAuditedTransferAttr>(S, D, Attr.getRange(),
Attr.getName()))
return;

D->addAttr(::new (S.Context)
Expand Down Expand Up @@ -4646,6 +4688,14 @@ static void handleNoSanitizeSpecificAttr(Sema &S, Decl *D,
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.
Expand Down Expand Up @@ -5090,6 +5140,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case AttributeList::AT_OpenCLImageAccess:
handleSimpleAttribute<OpenCLImageAccessAttr>(S, D, Attr);
break;
case AttributeList::AT_InternalLinkage:
handleInternalLinkageAttr(S, D, Attr);
break;

// Microsoft attributes:
case AttributeList::AT_MSNoVTable:
Expand Down
79 changes: 79 additions & 0 deletions clang/test/CodeGenCXX/attribute_internal_linkage.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 4 additions & 2 deletions clang/test/Sema/attr-coldhot.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ int bar() __attribute__((__cold__));
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}}
6 changes: 4 additions & 2 deletions clang/test/Sema/attr-notail.c
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
Loading

0 comments on commit ae6ebd3

Please sign in to comment.