Disable caching of visibility.

The testcase in pr14929 shows that this is extremely hard to do. If we choose
to apply the attribute, that causes the visibility of some decls to change and
that can happen really late (during codegen).

Current gcc warns and ignores the attribute in this testcase with a warning.
This suggest that the correct solution is to find a point in the compilation
where we can compute the visibility and
* assert it was never computed before
* reject any attempts to compute it again in the future (with warnings).

llvm-svn: 172305
This commit is contained in:
Rafael Espindola 2013-01-12 06:42:30 +00:00
parent a7252d836a
commit 19de5613ea
11 changed files with 124 additions and 87 deletions

View File

@ -109,6 +109,7 @@ class NamedDecl : public Decl {
private:
NamedDecl *getUnderlyingDeclImpl();
void verifyLinkage() const;
protected:
NamedDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName N)
@ -228,12 +229,6 @@ public:
"Enum truncated!");
}
bool operator==(const LinkageInfo &Other) {
return linkage_ == Other.linkage_ &&
visibility_ == Other.visibility_ &&
explicit_ == Other.explicit_;
}
static LinkageInfo external() {
return LinkageInfo();
}
@ -329,7 +324,7 @@ public:
/// \brief Clear the linkage cache in response to a change
/// to the declaration.
void ClearLVCache();
void ClearLinkageCache();
/// \brief Looks through UsingDecls and ObjCCompatibleAliasDecls for
/// the underlying named decl.
@ -3337,7 +3332,7 @@ void Redeclarable<decl_type>::setPreviousDeclaration(decl_type *PrevDecl) {
// First one will point to this one as latest.
First->RedeclLink = LatestDeclLink(static_cast<decl_type*>(this));
if (NamedDecl *ND = dyn_cast<NamedDecl>(static_cast<decl_type*>(this)))
ND->ClearLVCache();
ND->ClearLinkageCache();
}
// Inline function definitions.

View File

@ -242,7 +242,7 @@ private:
SourceLocation Loc;
/// DeclKind - This indicates which class this is.
unsigned DeclKind : 6;
unsigned DeclKind : 8;
/// InvalidDecl - This indicates a semantic error occurred.
unsigned InvalidDecl : 1;
@ -284,16 +284,15 @@ protected:
/// IdentifierNamespace - This specifies what IDNS_* namespace this lives in.
unsigned IdentifierNamespace : 12;
/// These fields are only valid for NamedDecls subclasses.
/// \brief Whether the \c CachedLinkage field is active.
///
/// \brief Nonzero if the cache (i.e. the bitfields here starting
/// with 'Cache') is valid. If so, then this is a
/// LangOptions::VisibilityMode+1.
mutable unsigned CacheValidAndVisibility : 2;
/// \brief the linkage of this declaration.
/// This field is only valid for NamedDecls subclasses.
mutable unsigned HasCachedLinkage : 1;
/// \brief If \c HasCachedLinkage, the linkage of this declaration.
///
/// This field is only valid for NamedDecls subclasses.
mutable unsigned CachedLinkage : 2;
/// \brief true if the visibility is explicit.
mutable unsigned CachedVisibilityExplicit : 1;
friend class ASTDeclWriter;
friend class ASTDeclReader;
@ -310,7 +309,7 @@ protected:
HasAttrs(false), Implicit(false), Used(false), Referenced(false),
Access(AS_none), FromASTFile(0), Hidden(0),
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
CacheValidAndVisibility(0)
HasCachedLinkage(0)
{
if (StatisticsEnabled) add(DK);
}
@ -320,7 +319,7 @@ protected:
HasAttrs(false), Implicit(false), Used(false), Referenced(false),
Access(AS_none), FromASTFile(0), Hidden(0),
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
CacheValidAndVisibility(0)
HasCachedLinkage(0)
{
if (StatisticsEnabled) add(DK);
}

View File

@ -1781,7 +1781,7 @@ public:
std::pair<Linkage,Visibility> getLinkageAndVisibility() const;
/// \brief Note that the linkage is no longer known.
void ClearLVCache();
void ClearLinkageCache();
const char *getTypeClassName() const;

View File

@ -958,7 +958,6 @@ ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl,
"Already noted what static data member was instantiated from");
InstantiatedFromStaticDataMember[Inst]
= new (*this) MemberSpecializationInfo(Tmpl, TSK, PointOfInstantiation);
Inst->ClearLVCache();
}
FunctionDecl *ASTContext::getClassScopeSpecializationPattern(

View File

@ -104,14 +104,8 @@ getLVForTemplateParameterList(const TemplateParameterList *Params) {
return LV;
}
/// Compute the linkage and visibility for the given declaration.
static LinkageInfo computeLVForDecl(const NamedDecl *D, bool OnlyTemplate);
static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate) {
if (!OnlyTemplate)
return D->getLinkageAndVisibility();
return computeLVForDecl(D, OnlyTemplate);
}
/// getLVForDecl - Get the linkage and visibility for the given declaration.
static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate);
/// \brief Get the most restrictive linkage for the types and
/// declarations in the given template argument list.
@ -575,18 +569,18 @@ static void clearLinkageForClass(const CXXRecordDecl *record) {
i = record->decls_begin(), e = record->decls_end(); i != e; ++i) {
Decl *child = *i;
if (isa<NamedDecl>(child))
cast<NamedDecl>(child)->ClearLVCache();
cast<NamedDecl>(child)->ClearLinkageCache();
}
}
void NamedDecl::anchor() { }
void NamedDecl::ClearLVCache() {
void NamedDecl::ClearLinkageCache() {
// Note that we can't skip clearing the linkage of children just
// because the parent doesn't have cached linkage: we don't cache
// when computing linkage for parent contexts.
CacheValidAndVisibility = 0;
HasCachedLinkage = 0;
// If we're changing the linkage of a class, we need to reset the
// linkage of child declarations, too.
@ -597,51 +591,66 @@ void NamedDecl::ClearLVCache() {
dyn_cast<ClassTemplateDecl>(const_cast<NamedDecl*>(this))) {
// Clear linkage for the template pattern.
CXXRecordDecl *record = temp->getTemplatedDecl();
record->CacheValidAndVisibility = 0;
record->HasCachedLinkage = 0;
clearLinkageForClass(record);
// We need to clear linkage for specializations, too.
for (ClassTemplateDecl::spec_iterator
i = temp->spec_begin(), e = temp->spec_end(); i != e; ++i)
i->ClearLVCache();
i->ClearLinkageCache();
}
// Clear cached linkage for function template decls, too.
if (FunctionTemplateDecl *temp =
dyn_cast<FunctionTemplateDecl>(const_cast<NamedDecl*>(this))) {
temp->getTemplatedDecl()->ClearLVCache();
temp->getTemplatedDecl()->ClearLinkageCache();
for (FunctionTemplateDecl::spec_iterator
i = temp->spec_begin(), e = temp->spec_end(); i != e; ++i)
i->ClearLVCache();
i->ClearLinkageCache();
}
}
Linkage NamedDecl::getLinkage() const {
return getLinkageAndVisibility().linkage();
if (HasCachedLinkage) {
assert(Linkage(CachedLinkage) ==
getLVForDecl(this, true).linkage());
return Linkage(CachedLinkage);
}
CachedLinkage = getLVForDecl(this, true).linkage();
HasCachedLinkage = 1;
#ifndef NDEBUG
verifyLinkage();
#endif
return Linkage(CachedLinkage);
}
LinkageInfo NamedDecl::getLinkageAndVisibility() const {
if (CacheValidAndVisibility) {
Linkage L = static_cast<Linkage>(CachedLinkage);
Visibility V = static_cast<Visibility>(CacheValidAndVisibility - 1);
bool Explicit = CachedVisibilityExplicit;
LinkageInfo LV(L, V, Explicit);
assert(LV == computeLVForDecl(this, false));
return LV;
LinkageInfo LI = getLVForDecl(this, false);
if (HasCachedLinkage) {
assert(Linkage(CachedLinkage) == LI.linkage());
return LI;
}
LinkageInfo LV = computeLVForDecl(this, false);
CachedLinkage = LV.linkage();
CacheValidAndVisibility = LV.visibility() + 1;
CachedVisibilityExplicit = LV.visibilityExplicit();
HasCachedLinkage = 1;
CachedLinkage = LI.linkage();
#ifndef NDEBUG
verifyLinkage();
#endif
return LI;
}
void NamedDecl::verifyLinkage() const {
// In C (because of gnu inline) and in c++ with microsoft extensions an
// static can follow an extern, so we can have two decls with different
// linkages.
const LangOptions &Opts = getASTContext().getLangOpts();
if (!Opts.CPlusPlus || Opts.MicrosoftExt)
return LV;
return;
// We have just computed the linkage for this decl. By induction we know
// that all other computed linkages match, check that the one we just computed
@ -651,15 +660,12 @@ LinkageInfo NamedDecl::getLinkageAndVisibility() const {
NamedDecl *T = cast<NamedDecl>(*I);
if (T == this)
continue;
if (T->CacheValidAndVisibility != 0) {
if (T->HasCachedLinkage != 0) {
D = T;
break;
}
}
assert(!D || D->CachedLinkage == CachedLinkage);
#endif
return LV;
}
llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const {
@ -723,7 +729,7 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const {
return llvm::Optional<Visibility>();
}
static LinkageInfo computeLVForDecl(const NamedDecl *D, bool OnlyTemplate) {
static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate) {
// Objective-C: treat all Objective-C declarations as having external
// linkage.
switch (D->getKind()) {
@ -1192,7 +1198,7 @@ VarDecl *VarDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
void VarDecl::setStorageClass(StorageClass SC) {
assert(isLegalForVariable(SC));
if (getStorageClass() != SC)
ClearLVCache();
ClearLinkageCache();
VarDeclBits.SClass = SC;
}
@ -1714,9 +1720,6 @@ void FunctionDecl::setBody(Stmt *B) {
Body = B;
if (B)
EndRangeLoc = B->getLocEnd();
for (redecl_iterator R = redecls_begin(), REnd = redecls_end(); R != REnd;
++R)
R->ClearLVCache();
}
void FunctionDecl::setPure(bool P) {
@ -1825,7 +1828,7 @@ FunctionDecl *FunctionDecl::getCanonicalDecl() {
void FunctionDecl::setStorageClass(StorageClass SC) {
assert(isLegalForFunction(SC));
if (getStorageClass() != SC)
ClearLVCache();
ClearLinkageCache();
SClass = SC;
}
@ -2595,8 +2598,8 @@ TagDecl* TagDecl::getCanonicalDecl() {
void TagDecl::setTypedefNameForAnonDecl(TypedefNameDecl *TDD) {
TypedefNameDeclOrQualifier = TDD;
if (TypeForDecl)
const_cast<Type*>(TypeForDecl)->ClearLVCache();
ClearLVCache();
const_cast<Type*>(TypeForDecl)->ClearLinkageCache();
ClearLinkageCache();
}
void TagDecl::startDefinition() {

View File

@ -2190,7 +2190,7 @@ std::pair<Linkage,Visibility> Type::getLinkageAndVisibility() const {
return std::make_pair(TypeBits.getLinkage(), TypeBits.getVisibility());
}
void Type::ClearLVCache() {
void Type::ClearLinkageCache() {
TypeBits.CacheValidAndVisibility = 0;
if (QualType(this, 0) != CanonicalType)
CanonicalType->TypeBits.CacheValidAndVisibility = 0;

View File

@ -321,7 +321,6 @@ void Sema::AddPushedVisibilityAttribute(Decl *D) {
= (VisibilityAttr::VisibilityType) rawType;
SourceLocation loc = Stack->back().second;
ND->ClearLVCache();
D->addAttr(::new (Context) VisibilityAttr(loc, Context, type));
}

View File

@ -1824,18 +1824,14 @@ DeclHasAttr(const Decl *D, const Attr *A) {
bool Sema::mergeDeclAttribute(NamedDecl *D, InheritableAttr *Attr) {
InheritableAttr *NewAttr = NULL;
if (AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attr)) {
if (AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attr))
NewAttr = mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(),
AA->getIntroduced(), AA->getDeprecated(),
AA->getObsoleted(), AA->getUnavailable(),
AA->getMessage());
if (NewAttr)
D->ClearLVCache();
} else if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr)) {
else if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr))
NewAttr = mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility());
if (NewAttr)
D->ClearLVCache();
} else if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr))
else if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr))
NewAttr = mergeDLLImportAttr(D, ImportA->getRange());
else if (DLLExportAttr *ExportA = dyn_cast<DLLExportAttr>(Attr))
NewAttr = mergeDLLExportAttr(D, ExportA->getRange());
@ -6695,7 +6691,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
}
VDecl->setTypeSourceInfo(DeducedType);
VDecl->setType(DeducedType->getType());
VDecl->ClearLVCache();
VDecl->ClearLinkageCache();
// In ARC, infer lifetime.
if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl))

View File

@ -2043,7 +2043,6 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range,
VersionTuple MergedDeprecated = Deprecated;
VersionTuple MergedObsoleted = Obsoleted;
bool FoundAny = false;
bool DroppedAny = false;
if (D->hasAttrs()) {
AttrVec &Attrs = D->getAttrs();
@ -2078,7 +2077,6 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range,
Diag(OldAA->getLocation(), diag::warn_mismatched_availability);
Diag(Range.getBegin(), diag::note_previous_attribute);
Attrs.erase(Attrs.begin() + i);
DroppedAny = true;
--e;
continue;
}
@ -2098,7 +2096,6 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range,
MergedIntroduced2, MergedDeprecated2,
MergedObsoleted2)) {
Attrs.erase(Attrs.begin() + i);
DroppedAny = true;
--e;
continue;
}
@ -2110,9 +2107,6 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range,
}
}
if (DroppedAny)
D->ClearLVCache();
if (FoundAny &&
MergedIntroduced == Introduced &&
MergedDeprecated == Deprecated &&
@ -2159,10 +2153,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D,
Deprecated.Version,
Obsoleted.Version,
IsUnavailable, Str);
if (NewAttr) {
if (NewAttr)
D->addAttr(NewAttr);
ND->ClearLVCache();
}
}
VisibilityAttr *Sema::mergeVisibilityAttr(Decl *D, SourceRange Range,
@ -2179,8 +2171,6 @@ VisibilityAttr *Sema::mergeVisibilityAttr(Decl *D, SourceRange Range,
Diag(ExistingAttr->getLocation(), diag::err_mismatched_visibility);
Diag(Range.getBegin(), diag::note_previous_attribute);
D->dropAttr<VisibilityAttr>();
NamedDecl *ND = cast<NamedDecl>(D);
ND->ClearLVCache();
}
return ::new (Context) VisibilityAttr(Range, Context, Vis);
}
@ -2224,11 +2214,8 @@ static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}
VisibilityAttr *NewAttr = S.mergeVisibilityAttr(D, Attr.getRange(), type);
if (NewAttr) {
if (NewAttr)
D->addAttr(NewAttr);
NamedDecl *ND = cast<NamedDecl>(D);
ND->ClearLVCache();
}
}
static void handleObjCMethodFamilyAttr(Sema &S, Decl *decl,

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown -fvisibility-inlines-hidden -emit-llvm -o - %s -O2 -disable-llvm-optzns | FileCheck %s
// RUN: %clang_cc1 -triple i386-unknown-unknown -std=c++11 -fvisibility-inlines-hidden -emit-llvm -o - %s -O2 -disable-llvm-optzns | FileCheck %s
// The trickery with optimization in the run line is to get IR
// generation to emit available_externally function bodies, but not
@ -147,3 +147,18 @@ namespace test5 {
template <int Idx_nocapture> void Op() {
}
}
namespace test6 {
// just don't crash.
template <typename T>
void f(T x) {
}
struct C {
static void g() {
f([](){});
}
};
void g() {
C::g();
}
}

View File

@ -1112,3 +1112,47 @@ namespace test60 {
// CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test604testINS_1bENS_1aEEEvv
}
}
namespace test61 {
template <typename T1>
struct Class1
{
void f1() { f2(); }
inline void f2();
};
template<>
inline void Class1<int>::f2()
{
}
void g(Class1<int> *x) {
x->f1();
}
}
namespace test61 {
// Just test that we don't crash. Currently we apply this attribute. Current
// gcc issues a warning about it being unused since "the type is already
// defined". We should probably do the same.
template class __attribute__ ((visibility("hidden"))) Class1<int>;
}
namespace test62 {
template <typename T1>
struct Class1
{
void f1() { f2(); }
inline void f2() {}
};
template<>
inline void Class1<int>::f2()
{
}
void g(Class1<int> *x) {
x->f2();
}
}
namespace test62 {
template class __attribute__ ((visibility("hidden"))) Class1<int>;
// Just test that we don't crash. Currently we apply this attribute. Current
// gcc issues a warning about it being unused since "the type is already
// defined". We should probably do the same.
}