Provide the correct mangling and linkage for certain unnamed nested classes.

This corrects the mangling and linkage of classes (& their member functions) in
cases like this:

  struct foo {
    struct {
      void func() { ... }
    } x;
  };

we were accidentally giving this nested unnamed struct 'no' linkage where it
should've had the linkage of the outer class. The mangling was incorrecty too,
mangling as TU-wide unnamed type mangling of $_X rather than class-scoped
mangling of UtX_.

This also fixes -Wunused-member-function which would incorrectly diagnose
'func' as unused due to it having no linkage & thus appearing to be TU-local
when in fact it might be correctly used in another TU.

Similar mangling should be applied to function local classes in similar cases
but I've deferred that for a subsequent patch.

Review/discussion by Richard Smith, John McCall, & especially Eli Friedman.

llvm-svn: 167906
This commit is contained in:
David Blaikie 2012-11-14 01:52:05 +00:00
parent 5f265fb124
commit 095deba533
9 changed files with 93 additions and 18 deletions

View File

@ -343,7 +343,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// \brief Mapping from each declaration context to its corresponding lambda
/// mangling context.
llvm::DenseMap<const DeclContext *, LambdaMangleContext> LambdaMangleContexts;
llvm::DenseMap<const DeclContext *, unsigned> UnnamedMangleContexts;
llvm::DenseMap<const TagDecl *, unsigned> UnnamedMangleNumbers;
/// \brief Mapping that stores parameterIndex values for ParmVarDecls when
/// that value exceeds the bitfield size of ParmVarDeclBits.ParameterIndex.
typedef llvm::DenseMap<const VarDecl *, unsigned> ParameterIndexTable;
@ -1989,6 +1992,9 @@ public:
/// it is not used.
bool DeclMustBeEmitted(const Decl *D);
void addUnnamedTag(const TagDecl *Tag);
int getUnnamedTagManglingNumber(const TagDecl *Tag) const;
/// \brief Retrieve the lambda mangling number for a lambda expression.
unsigned getLambdaManglingNumber(CXXMethodDecl *CallOperator);

View File

@ -377,16 +377,16 @@ private:
static bool isExprRep(TST T) {
return (T == TST_typeofExpr || T == TST_decltype);
}
DeclSpec(const DeclSpec &) LLVM_DELETED_FUNCTION;
void operator=(const DeclSpec &) LLVM_DELETED_FUNCTION;
public:
static bool isDeclRep(TST T) {
return (T == TST_enum || T == TST_struct ||
T == TST_interface || T == TST_union ||
T == TST_class);
}
DeclSpec(const DeclSpec &) LLVM_DELETED_FUNCTION;
void operator=(const DeclSpec &) LLVM_DELETED_FUNCTION;
public:
DeclSpec(AttributeFactory &attrFactory)
: StorageClassSpec(SCS_unspecified),
SCS_thread_specified(false),

View File

@ -7508,6 +7508,23 @@ size_t ASTContext::getSideTableAllocatedMemory() const {
+ llvm::capacity_in_bytes(ClassScopeSpecializationPattern);
}
void ASTContext::addUnnamedTag(const TagDecl *Tag) {
// FIXME: This mangling should be applied to function local classes too
if (!Tag->getName().empty() || Tag->getTypedefNameForAnonDecl() ||
!isa<CXXRecordDecl>(Tag->getParent()) || Tag->getLinkage() != ExternalLinkage)
return;
std::pair<llvm::DenseMap<const DeclContext *, unsigned>::iterator, bool> P =
UnnamedMangleContexts.insert(std::make_pair(Tag->getParent(), 0));
UnnamedMangleNumbers.insert(std::make_pair(Tag, P.first->second++));
}
int ASTContext::getUnnamedTagManglingNumber(const TagDecl *Tag) const {
llvm::DenseMap<const TagDecl *, unsigned>::const_iterator I =
UnnamedMangleNumbers.find(Tag);
return I != UnnamedMangleNumbers.end() ? I->second : -1;
}
unsigned ASTContext::getLambdaManglingNumber(CXXMethodDecl *CallOperator) {
CXXRecordDecl *Lambda = CallOperator->getParent();
return LambdaMangleContexts[Lambda->getDeclContext()]

View File

@ -480,8 +480,7 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, bool OnlyTemplate) {
if (!(isa<CXXMethodDecl>(D) ||
isa<VarDecl>(D) ||
isa<FieldDecl>(D) ||
(isa<TagDecl>(D) &&
(D->getDeclName() || cast<TagDecl>(D)->getTypedefNameForAnonDecl()))))
isa<TagDecl>(D)))
return LinkageInfo::none();
LinkageInfo LV;
@ -2561,8 +2560,7 @@ void TagDecl::setTypedefNameForAnonDecl(TypedefNameDecl *TDD) {
void TagDecl::startDefinition() {
IsBeingDefined = true;
if (isa<CXXRecordDecl>(this)) {
CXXRecordDecl *D = cast<CXXRecordDecl>(this);
if (CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(this)) {
struct CXXRecordDecl::DefinitionData *Data =
new (getASTContext()) struct CXXRecordDecl::DefinitionData(D);
for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I)

View File

@ -1117,6 +1117,18 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
break;
}
}
int UnnamedMangle = Context.getASTContext().getUnnamedTagManglingNumber(TD);
if (UnnamedMangle != -1) {
Out << "Ut";
if (UnnamedMangle != 0)
Out << llvm::utostr(UnnamedMangle - 1);
Out << '_';
break;
}
//assert(cast<RecordDecl>(RD)->isAnonymousStructOrUnion() && "Don't mangle unnamed things as "
// "anonymous things");
// Get a unique id for the anonymous struct.
uint64_t AnonStructId = Context.getAnonymousStructId(TD);

View File

@ -2655,6 +2655,7 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
}
if (Tag) {
getASTContext().addUnnamedTag(Tag);
Tag->setFreeStanding();
if (Tag->isInvalidDecl())
return Tag;
@ -7355,6 +7356,10 @@ Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
if (Decl *D = Group[i])
Decls.push_back(D);
if (DeclSpec::isDeclRep(DS.getTypeSpecType()))
if (const TagDecl *Tag = dyn_cast_or_null<TagDecl>(DS.getRepAsDecl()))
getASTContext().addUnnamedTag(Tag);
return BuildDeclaratorGroup(Decls.data(), Decls.size(),
DS.getTypeSpecType() == DeclSpec::TST_auto);
}

View File

@ -218,7 +218,7 @@ struct S7 {
// PR5139
// CHECK: @_ZN2S7C1Ev
// CHECK: @_ZN2S7C2Ev
// CHECK: @"_ZN2S73$_0C1Ev"
// CHECK: @_ZN2S7Ut_C1Ev
S7::S7() {}
// PR5063
@ -852,3 +852,23 @@ namespace test36 {
// CHECK: define weak_odr {{.*}} @_ZN6test362f1IJifEEENS_1AIXsZfp_EEEDpT_
template A<2> f1(int, float);
}
namespace test37 {
struct foo {
struct {
} a;
typedef struct { } b;
typedef struct { } *c;
struct {
} d;
};
template<typename T> void func(T) { }
void test() {
// CHECK: define linkonce_odr void @_ZN6test374funcINS_3fooUt_EEEvT_
func(foo().a);
// CHECK: define linkonce_odr void @_ZN6test374funcINS_3fooUt0_EEEvT_
func(*foo::c());
// CHECK: define linkonce_odr void @_ZN6test374funcINS_3fooUt1_EEEvT_
func(foo().d);
}
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -w -o - | FileCheck %s
struct S {
enum { FOO = 42 };
@ -17,21 +17,21 @@ template <typename T> int f(T t) {
}
void test() {
// Look for two instantiations, entirely internal to this TU, one for FOO's
// Look for two instantiations, one for FOO's
// type and one for BAR's.
// CHECK: define internal i32 @"_Z1fIN1S3$_0EEiT_"(i32 %t)
// CHECK: define linkonce_odr i32 @_Z1fIN1SUt_EEiT_(i32 %t)
(void)f(S::FOO);
// CHECK: define internal i32 @"_Z1fIN1S3$_1EEiT_"(i32 %t)
// CHECK: define linkonce_odr i32 @_Z1fIN1SUt0_EEiT_(i32 %t)
(void)f(S::BAR);
// Now check for the class template instantiations. Annoyingly, they are in
// reverse order.
//
// BAR's instantiation of X:
// CHECK: define internal i32 @"_ZN1XIN1S3$_1EE1fEv"(%struct.X* %this)
// CHECK: define internal void @"_ZN1XIN1S3$_1EEC2ES1_"(%struct.X* %this, i32 %t) unnamed_addr
// CHECK: define linkonce_odr i32 @_ZN1XIN1SUt0_EE1fEv(%struct.X* %this)
// CHECK: define linkonce_odr void @_ZN1XIN1SUt0_EEC2ES1_(%struct.X* %this, i32 %t) unnamed_addr
//
// FOO's instantiation of X:
// CHECK: define internal i32 @"_ZN1XIN1S3$_0EE1fEv"(%struct.X.0* %this)
// CHECK: define internal void @"_ZN1XIN1S3$_0EEC2ES1_"(%struct.X.0* %this, i32 %t) unnamed_addr
// CHECK: define linkonce_odr i32 @_ZN1XIN1SUt_EE1fEv(%struct.X.0* %this)
// CHECK: define linkonce_odr void @_ZN1XIN1SUt_EEC2ES1_(%struct.X.0* %this, i32 %t) unnamed_addr
}

View File

@ -101,3 +101,20 @@ namespace test5 {
static const double d = 0.0;
int y = sizeof(d);
}
namespace unused_nested {
class outer {
void func1();
struct {
void func2() {
}
} x;
};
}
namespace unused {
struct {
void func() { // expected-warning {{unused member function}}
}
} x; // expected-warning {{unused variable}}
}