return DBuilder.createSubroutineType(Unit, EltTypeArray);
}
+/// Convert an AccessSpecifier into the corresponding DIDescriptor flag.
+/// As an optimization, return 0 if the access specifier equals the
+/// default for the containing type.
+static unsigned getAccessFlag(AccessSpecifier Access, const RecordDecl *RD) {
+ AccessSpecifier Default = clang::AS_none;
+ if (RD && RD->isClass())
+ Default = clang::AS_private;
+ else if (RD && (RD->isStruct() || RD->isUnion()))
+ Default = clang::AS_public;
+
+ if (Access == Default)
+ return 0;
+
+ switch(Access) {
+ case clang::AS_private: return llvm::DIDescriptor::FlagPrivate;
+ case clang::AS_protected: return llvm::DIDescriptor::FlagProtected;
+ case clang::AS_public: return llvm::DIDescriptor::FlagPublic;
+ case clang::AS_none: return 0;
+ }
+ llvm_unreachable("unexpected access enumerator");
+}
llvm::DIType CGDebugInfo::createFieldType(StringRef name,
QualType type,
AccessSpecifier AS,
uint64_t offsetInBits,
llvm::DIFile tunit,
- llvm::DIScope scope) {
+ llvm::DIScope scope,
+ const RecordDecl* RD) {
llvm::DIType debugType = getOrCreateType(type, tunit);
// Get the location for the field.
SizeInBits = sizeInBitsOverride;
}
- unsigned flags = 0;
- if (AS == clang::AS_private)
- flags |= llvm::DIDescriptor::FlagPrivate;
- else if (AS == clang::AS_protected)
- flags |= llvm::DIDescriptor::FlagProtected;
-
+ unsigned flags = getAccessFlag(AS, RD);
return DBuilder.createMemberType(scope, name, file, line, SizeInBits,
AlignInBits, offsetInBits, flags, debugType);
}
llvm::DIType fieldType
= createFieldType(VName, Field->getType(), SizeInBitsOverride,
C.getLocation(), Field->getAccess(),
- layout.getFieldOffset(fieldno), VUnit, RecordTy);
+ layout.getFieldOffset(fieldno), VUnit, RecordTy,
+ CXXDecl);
elements.push_back(fieldType);
} else if (C.capturesThis()) {
// TODO: Need to handle 'this' in some way by probably renaming the
QualType type = f->getType();
llvm::DIType fieldType
= createFieldType("this", type, 0, f->getLocation(), f->getAccess(),
- layout.getFieldOffset(fieldno), VUnit, RecordTy);
+ layout.getFieldOffset(fieldno), VUnit, RecordTy,
+ CXXDecl);
elements.push_back(fieldType);
}
/// Helper for CollectRecordFields.
llvm::DIDerivedType
CGDebugInfo::CreateRecordStaticField(const VarDecl *Var,
- llvm::DIType RecordTy) {
+ llvm::DIType RecordTy,
+ const RecordDecl* RD) {
// Create the descriptor for the static variable, with or without
// constant initializers.
llvm::DIFile VUnit = getOrCreateFile(Var->getLocation());
}
}
- unsigned Flags = 0;
- AccessSpecifier Access = Var->getAccess();
- if (Access == clang::AS_private)
- Flags |= llvm::DIDescriptor::FlagPrivate;
- else if (Access == clang::AS_protected)
- Flags |= llvm::DIDescriptor::FlagProtected;
-
+ unsigned Flags = getAccessFlag(Var->getAccess(), RD);
llvm::DIDerivedType GV = DBuilder.createStaticMemberType(
RecordTy, VName, VUnit, LineNumber, VTy, Flags, C);
StaticDataMemberCache[Var->getCanonicalDecl()] = llvm::WeakVH(GV);
CollectRecordNormalField(const FieldDecl *field, uint64_t OffsetInBits,
llvm::DIFile tunit,
SmallVectorImpl<llvm::Value *> &elements,
- llvm::DIType RecordTy) {
+ llvm::DIType RecordTy,
+ const RecordDecl* RD) {
StringRef name = field->getName();
QualType type = field->getType();
llvm::DIType fieldType
= createFieldType(name, type, SizeInBitsOverride,
field->getLocation(), field->getAccess(),
- OffsetInBits, tunit, RecordTy);
+ OffsetInBits, tunit, RecordTy, RD);
elements.push_back(fieldType);
}
"Static data member declaration should still exist");
elements.push_back(
llvm::DIDerivedType(cast<llvm::MDNode>(MI->second)));
- } else
- elements.push_back(CreateRecordStaticField(V, RecordTy));
+ } else {
+ auto Field = CreateRecordStaticField(V, RecordTy, record);
+ elements.push_back(Field);
+ }
} else if (const auto *field = dyn_cast<FieldDecl>(I)) {
CollectRecordNormalField(field, layout.getFieldOffset(fieldNo),
- tunit, elements, RecordTy);
+ tunit, elements, RecordTy, record);
// Bump field number for next field.
++fieldNo;
unsigned Flags = 0;
if (Method->isImplicit())
Flags |= llvm::DIDescriptor::FlagArtificial;
- AccessSpecifier Access = Method->getAccess();
- if (Access == clang::AS_private)
- Flags |= llvm::DIDescriptor::FlagPrivate;
- else if (Access == clang::AS_protected)
- Flags |= llvm::DIDescriptor::FlagProtected;
+ Flags |= getAccessFlag(Method->getAccess(), Method->getParent());
if (const CXXConstructorDecl *CXXC = dyn_cast<CXXConstructorDecl>(Method)) {
if (CXXC->isExplicit())
Flags |= llvm::DIDescriptor::FlagExplicit;
// FIXME: Inconsistent units for BaseOffset. It is in bytes when
// BI->isVirtual() and bits when not.
- AccessSpecifier Access = BI.getAccessSpecifier();
- if (Access == clang::AS_private)
- BFlags |= llvm::DIDescriptor::FlagPrivate;
- else if (Access == clang::AS_protected)
- BFlags |= llvm::DIDescriptor::FlagProtected;
-
+ BFlags |= getAccessFlag(BI.getAccessSpecifier(), RD);
llvm::DIType DTy =
DBuilder.createInheritance(RecordTy,
getOrCreateType(BI.getType(), Unit),
Flags = llvm::DIDescriptor::FlagProtected;
else if (Field->getAccessControl() == ObjCIvarDecl::Private)
Flags = llvm::DIDescriptor::FlagPrivate;
+ else if (Field->getAccessControl() == ObjCIvarDecl::Public)
+ Flags = llvm::DIDescriptor::FlagPublic;
llvm::MDNode *PropertyNode = nullptr;
if (ObjCImplementationDecl *ImpD = ID->getImplementation()) {
// If the member wasn't found in the cache, lazily construct and add it to the
// type (used when a limited form of the type is emitted).
- llvm::DICompositeType Ctxt(
- getContextDescriptor(cast<Decl>(D->getDeclContext())));
- llvm::DIDerivedType T = CreateRecordStaticField(D, Ctxt);
- return T;
+ auto DC = D->getDeclContext();
+ llvm::DICompositeType Ctxt(getContextDescriptor(cast<Decl>(DC)));
+ return CreateRecordStaticField(D, Ctxt, cast<RecordDecl>(DC));
}
/// Recursively collect all of the member fields of a global anonymous decl and
llvm::DIType createFieldType(StringRef name, QualType type,
uint64_t sizeInBitsOverride, SourceLocation loc,
- AccessSpecifier AS, uint64_t offsetInBits,
+ AccessSpecifier AS,
+ uint64_t offsetInBits,
llvm::DIFile tunit,
- llvm::DIScope scope);
+ llvm::DIScope scope,
+ const RecordDecl* RD = nullptr);
// Helpers for collecting fields of a record.
void CollectRecordLambdaFields(const CXXRecordDecl *CXXDecl,
SmallVectorImpl<llvm::Value *> &E,
llvm::DIType RecordTy);
llvm::DIDerivedType CreateRecordStaticField(const VarDecl *Var,
- llvm::DIType RecordTy);
+ llvm::DIType RecordTy,
+ const RecordDecl* RD);
void CollectRecordNormalField(const FieldDecl *Field, uint64_t OffsetInBits,
llvm::DIFile F,
SmallVectorImpl<llvm::Value *> &E,
- llvm::DIType RecordTy);
+ llvm::DIType RecordTy,
+ const RecordDecl* RD);
void CollectRecordFields(const RecordDecl *Decl, llvm::DIFile F,
SmallVectorImpl<llvm::Value *> &E,
llvm::DICompositeType RecordTy);
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm -g %s -o - | FileCheck %s
+// Test the various accessibility flags in the debug info.
+struct A {
+ // CHECK-DAG: [ DW_TAG_subprogram ] [line [[@LINE+1]]] [pub_default]
+ void pub_default();
+ // CHECK-DAG: [ DW_TAG_member ] [pub_default_static] [line [[@LINE+1]]{{.*}}offset 0] [static]
+ static int pub_default_static;
+};
+
+// CHECK: [ DW_TAG_inheritance ] {{.*}} [public] [from {{.*}}A]
+class B : public A {
+public:
+ // CHECK-DAG: [ DW_TAG_subprogram ] [line [[@LINE+1]]] [public] [pub]
+ void pub();
+ // CHECK-DAG: [ DW_TAG_member ] [public_static] [line [[@LINE+1]]{{.*}} [public] [static]
+ static int public_static;
+protected:
+ // CHECK: [ DW_TAG_subprogram ] [line [[@LINE+1]]] [protected] [prot]
+ void prot();
+private:
+ // CHECK: [ DW_TAG_subprogram ] [line [[@LINE+1]]] [priv_default]
+ void priv_default();
+};
+
+union U {
+ // CHECK-DAG: [ DW_TAG_subprogram ] [line [[@LINE+1]]] [union_pub_default]
+ void union_pub_default();
+private:
+ // CHECK-DAG: [ DW_TAG_member ] [union_priv] [line [[@LINE+1]]{{.*}} [private]
+ int union_priv;
+};
+
+
+// CHECK: i32 256, {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+1]]] [def] [free]
+void free() {}
+
+A a;
+B b;
+U u;
// CHECK: ![[CLASSTYPE:.*]] = {{.*}}, metadata !"_ZTS1A"} ; [ DW_TAG_class_type ] [A]
// CHECK: ![[ARTARG:.*]] = {{.*}} ; [ DW_TAG_pointer_type ] [line 0, size 64, align 64, offset 0] [artificial] [from _ZTS1A]
-// CHECK: metadata !"_ZTS1A", {{.*}} ; [ DW_TAG_subprogram ] [line 12] [A]
+// CHECK: metadata !"_ZTS1A", {{.*}} ; [ DW_TAG_subprogram ] [line 12] [public] [A]
// CHECK: metadata [[FUNCTYPE:![0-9]*]], i32 0, null, null, null} ; [ DW_TAG_subroutine_type ]
// CHECK: [[FUNCTYPE]] = metadata !{null, metadata ![[ARTARG]], metadata !{{.*}}, metadata !{{.*}}}
public:
InnerClass(); // Here createContextChain() generates a limited type for OuterClass.
} theInnerClass;
-// CHECK0: [[DECL:[0-9]+]] = {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+1]]] [private] [OuterClass]
+// CHECK0: [[DECL:[0-9]+]] = {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+1]]] [OuterClass]
OuterClass(const Foo *); // line 10
};
OuterClass::InnerClass OuterClass::theInnerClass; // This toplevel decl causes InnerClass to be generated.
public:
InnerClass1();
} theInnerClass1;
-// CHECK1: [[DECL:[0-9]+]] = {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+2]]] [private] [Bar]
+// CHECK1: [[DECL:[0-9]+]] = {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+2]]] [Bar]
// CHECK1: metadata {{.*}}, metadata ![[DECL]], metadata {{.*}}, i32 [[@LINE+4]]} ; [ DW_TAG_subprogram ] [line [[@LINE+4]]] [def] [Bar]
void Bar(const Foo1 *);
};
public:
InnerClass2();
} theInnerClass2;
-// CHECK2: [[DECL:[0-9]+]] = {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+1]]] [private] [~OuterClass2]
+// CHECK2: [[DECL:[0-9]+]] = {{.*}} ; [ DW_TAG_subprogram ] [line [[@LINE+1]]] [~OuterClass2]
~OuterClass2(); // line 10
};
OuterClass2::InnerClass2 OuterClass2::theInnerClass2;
// Test (r)value and CVR qualifiers on C++11 non-static member functions.
class A {
public:
- // CHECK: i32 [[@LINE+2]], metadata ![[PLSR:[0-9]+]], {{.*}}[ DW_TAG_subprogram ] [line [[@LINE+2]]] [reference] [l]
+ // CHECK: i32 [[@LINE+2]], metadata ![[PLSR:[0-9]+]], {{.*}}[ DW_TAG_subprogram ] [line [[@LINE+2]]] [public] [reference] [l]
// CHECK: ![[PLSR]] ={{.*}}[ DW_TAG_subroutine_type ]{{.*}}[reference]
void l() const &;
// CHECK: ![[ARGS:[0-9]+]] = metadata !{null, metadata ![[THIS:[0-9]+]]}
// CHECK: ![[THIS]] = {{.*}} metadata ![[CONST_A:.*]]} ; [ DW_TAG_pointer_type ]
// CHECK: ![[CONST_A]] = {{.*}} [ DW_TAG_const_type ]
- // CHECK: i32 [[@LINE+2]], metadata ![[PRSR:[0-9]+]], {{.*}}[ DW_TAG_subprogram ] [line [[@LINE+2]]] [rvalue reference] [r]
+ // CHECK: i32 [[@LINE+2]], metadata ![[PRSR:[0-9]+]], {{.*}}[ DW_TAG_subprogram ] [line [[@LINE+2]]] [public] [rvalue reference] [r]
// CHECK: ![[PRSR]] ={{.*}}metadata ![[ARGS]], i32 0, null, null, null}{{.*}}[ DW_TAG_subroutine_type ]{{.*}}[rvalue reference]
void r() const &&;
};
// CHECK: metadata !"_ZTS1X"} ; [ DW_TAG_enumeration_type ] [X]
// CHECK: metadata !"_ZTS1C"} ; [ DW_TAG_class_type ] [C]
-// CHECK: ![[DECL_A:[0-9]+]] = metadata {{.*}} [ DW_TAG_member ] [a] [line {{.*}}, size 0, align 0, offset 0] [private] [static]
-// CHECK: metadata !"const_a", {{.*}}, i1 true} ; [ DW_TAG_member ] [const_a] [line {{.*}}, size 0, align 0, offset 0] [private] [static]
+// CHECK: ![[DECL_A:[0-9]+]] = metadata {{.*}} [ DW_TAG_member ] [a] [line {{.*}}, size 0, align 0, offset 0] [static]
+// CHECK: metadata !"const_a", {{.*}}, i1 true} ; [ DW_TAG_member ] [const_a] [line {{.*}}, size 0, align 0, offset 0] [static]
// CHECK: ![[DECL_B:[0-9]+]] {{.*}} metadata !"b", {{.*}} [ DW_TAG_member ] [b] [line {{.*}}, size 0, align 0, offset 0] [protected] [static]
// CHECK: metadata !"const_b", {{.*}}, float 0x{{.*}}} ; [ DW_TAG_member ] [const_b] [line {{.*}}, size 0, align 0, offset 0] [protected] [static]
-// CHECK: ![[DECL_C:[0-9]+]] {{.*}} metadata !"c", {{.*}} [ DW_TAG_member ] [c] [line {{.*}}, size 0, align 0, offset 0] [static]
-// CHECK: metadata !"const_c", {{.*}} [ DW_TAG_member ] [const_c] [line {{.*}}, size 0, align 0, offset 0] [static]
-// CHECK: metadata !"x_a", {{.*}} [ DW_TAG_member ] [x_a] {{.*}} [static]
+// CHECK: ![[DECL_C:[0-9]+]] {{.*}} metadata !"c", {{.*}} [ DW_TAG_member ] [c] [line {{.*}}, size 0, align 0, offset 0] [public] [static]
+// CHECK: metadata !"const_c", {{.*}} [ DW_TAG_member ] [const_c] [line {{.*}}, size 0, align 0, offset 0] [public] [static]
+// CHECK: metadata !"x_a", {{.*}} [ DW_TAG_member ] [x_a] {{.*}} [public] [static]
// CHECK: metadata !"a", {{.*}} @_ZN1C1aE, metadata ![[DECL_A]]} ; [ DW_TAG_variable ] [a] {{.*}} [def]
// CHECK: metadata !"b", {{.*}} @_ZN1C1bE, metadata ![[DECL_B]]} ; [ DW_TAG_variable ] [b] {{.*}} [def]
// CHECK: metadata !"c", {{.*}} @_ZN1C1cE, metadata ![[DECL_C]]} ; [ DW_TAG_variable ] [c] {{.*}} [def]
// CHECK: [[LAM_D_ARGS]] = metadata !{metadata [[CAP_D_X:.*]], metadata [[CAP_D_Y:.*]], metadata [[CON_LAM_D:.*]]}
// CHECK: [[CAP_D_X]] = {{.*}}, metadata [[LAM_D]], {{.*}} [ DW_TAG_member ] [x] [line [[D_LINE]],
// CHECK: [[CAP_D_Y]] = {{.*}}, metadata [[LAM_D]], {{.*}} [ DW_TAG_member ] [y] [line [[D_LINE]],
-// CHECK: [[CON_LAM_D]] = {{.*}}, metadata [[LAM_D]], {{.*}} [ DW_TAG_subprogram ] [line [[D_LINE]]] [operator()]
+// CHECK: [[CON_LAM_D]] = {{.*}}, metadata [[LAM_D]], {{.*}} [ DW_TAG_subprogram ] [line [[D_LINE]]] [public] [operator()]
// Back to C. -- 55
// CHECK: [[LAM_C_ARGS]] = metadata !{metadata [[CAP_C:.*]], metadata [[CON_LAM_C:.*]]}
// Ignoring the member type for now.
// CHECK: [[CAP_C]] = {{.*}}, metadata [[LAM_C]], {{.*}}} ; [ DW_TAG_member ] [x] [line [[C_LINE]],
-// CHECK: [[CON_LAM_C]] = {{.*}}, metadata [[LAM_C]], {{.*}} [ DW_TAG_subprogram ] [line [[C_LINE]]] [operator()]
+// CHECK: [[CON_LAM_C]] = {{.*}}, metadata [[LAM_C]], {{.*}} [ DW_TAG_subprogram ] [line [[C_LINE]]] [public] [operator()]
// Back to B. -- 67
// CHECK: [[LAM_B:.*]] = {{.*}}, metadata [[B_FUNC]], {{.*}}, metadata [[LAM_B_ARGS:.*]], i32 0, null, null, null} ; [ DW_TAG_class_type ] [line [[B_LINE]],
// CHECK: [[LAM_B_ARGS]] = metadata !{metadata [[CAP_B:.*]], metadata [[CON_LAM_B:.*]]}
// CHECK: [[CAP_B]] = {{.*}}, metadata [[LAM_B]], {{.*}}} ; [ DW_TAG_member ] [x] [line [[B_LINE]],
-// CHECK: [[CON_LAM_B]] = {{.*}}, metadata [[LAM_B]], {{.*}} [ DW_TAG_subprogram ] [line [[B_LINE]]] [operator()]
+// CHECK: [[CON_LAM_B]] = {{.*}}, metadata [[LAM_B]], {{.*}} [ DW_TAG_subprogram ] [line [[B_LINE]]] [public] [operator()]
// Back to A. -- 78
// CHECK: [[LAM_A:.*]] = {{.*}}, metadata [[A_FUNC]], {{.*}}, metadata [[LAM_A_ARGS:.*]], i32 0, null, null, null} ; [ DW_TAG_class_type ] [line [[A_LINE]],
// CHECK: [[LAM_A_ARGS]] = metadata !{metadata [[CON_LAM_A:.*]]}
-// CHECK: [[CON_LAM_A]] = {{.*}}, metadata [[LAM_A]], {{.*}} [ DW_TAG_subprogram ] [line [[A_LINE]]] [operator()]
+// CHECK: [[CON_LAM_A]] = {{.*}}, metadata [[LAM_A]], {{.*}} [ DW_TAG_subprogram ] [line [[A_LINE]]] [public] [operator()]
// CVAR:
// CHECK: {{.*}} metadata [[CVAR_T:![0-9]*]], {{.*}} ; [ DW_TAG_variable ] [cvar] [line [[CVAR_LINE:[0-9]*]]]
}();
}
-// CHECK: {{.*}} [ DW_TAG_member ] [this] [line 11, size 64, align 64, offset 0] [private] [from ]
+// CHECK: {{.*}} [ DW_TAG_member ] [this] [line 11, size 64, align 64, offset 0] [from ]
// RUN: %clang -g -S -emit-llvm %s -o - | FileCheck %s
-// CHECK: [ DW_TAG_member ] [p] [{{[^]]*}}] [from int]
-// CHECK: [ DW_TAG_member ] [pr] [{{[^]]*}}] [private] [from int]
+// CHECK: [ DW_TAG_member ] [p] [{{[^]]*}}] [public] [from int]
+// CHECK: [ DW_TAG_member ] [pr] [{{[^]]*}}] [from int]
class A {
public:
// CHECK: {{.*}} [ DW_TAG_structure_type ] [I]
// Check for "a".
-// CHECK: {{.*}} [ DW_TAG_member ] [a] [line 7, size 32, align 32, offset 0] [from int]
+// CHECK: {{.*}} [ DW_TAG_member ] [a] [line 7, size 32, align 32, offset 0] [public] [from int]
// Make sure we don't output the same type twice.
// CHECK-NOT: {{.*}} [ DW_TAG_structure_type ] [I]
// Check for "b".
-// CHECK: {{.*}} [ DW_TAG_member ] [b] [line 18, size 32, align 32, offset 0] [from int]
+// CHECK: {{.*}} [ DW_TAG_member ] [b] [line 18, size 32, align 32, offset 0] [public] [from int]