[libclang] Expose record layout info via new libclang functions:
clang_Type_getAlignOf
clang_Type_getSizeOf
clang_Type_getOffsetOf
clang_Cursor_isBitField
Patch by Loïc Jaquemet!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179251 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index 70f4f36..36761bd 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -1314,6 +1314,18 @@
"""
return TokenGroup.get_tokens(self._tu, self.extent)
+ def is_bitfield(self):
+ """
+ Check if the field is a bitfield.
+ """
+ return conf.lib.clang_Cursor_isBitField(self)
+
+ def get_bitfield_width(self):
+ """
+ Retrieve the width of a bitfield.
+ """
+ return conf.lib.clang_getFieldDeclBitWidth(self)
+
@staticmethod
def from_result(res, fn, args):
assert isinstance(res, Cursor)
@@ -1613,6 +1625,24 @@
"""
return conf.lib.clang_getArraySize(self)
+ def get_align(self):
+ """
+ Retrieve the alignment of the record.
+ """
+ return conf.lib.clang_Type_getAlignOf(self)
+
+ def get_size(self):
+ """
+ Retrieve the size of the record.
+ """
+ return conf.lib.clang_Type_getSizeOf(self)
+
+ def get_offset(self, fieldname):
+ """
+ Retrieve the offset of a field in the record.
+ """
+ return conf.lib.clang_Type_getOffsetOf(self, c_char_p(fieldname))
+
def __eq__(self, other):
if type(other) != type(self):
return False
@@ -2623,6 +2653,10 @@
[Type],
c_longlong),
+ ("clang_getFieldDeclBitWidth",
+ [Cursor],
+ c_int),
+
("clang_getCanonicalCursor",
[Cursor],
Cursor,
@@ -3038,6 +3072,22 @@
[Cursor, c_uint],
Cursor,
Cursor.from_result),
+
+ ("clang_Cursor_isBitField",
+ [Cursor],
+ c_long),
+
+ ("clang_Type_getAlignOf",
+ [Type],
+ c_longlong),
+
+ ("clang_Type_getOffsetOf",
+ [Type, c_char_p],
+ c_longlong),
+
+ ("clang_Type_getSizeOf",
+ [Type],
+ c_ulonglong),
]
class LibclangError(Exception):
diff --git a/bindings/python/tests/cindex/test_type.py b/bindings/python/tests/cindex/test_type.py
index 28e4411..9bbed5a 100644
--- a/bindings/python/tests/cindex/test_type.py
+++ b/bindings/python/tests/cindex/test_type.py
@@ -298,3 +298,66 @@
assert isinstance(i.type.is_restrict_qualified(), bool)
assert i.type.is_restrict_qualified()
assert not j.type.is_restrict_qualified()
+
+def test_record_layout():
+ """Ensure Cursor.type.get_size, Cursor.type.get_align and
+ Cursor.type.get_offset works."""
+
+ source ="""
+struct a {
+ long a1;
+ long a2:3;
+ long a3:4;
+ long long a4;
+};
+"""
+ tries=[(['-target','i386-linux-gnu'],(4,16,0,32,35,64)),
+ (['-target','nvptx64-unknown-unknown'],(8,24,0,64,67,128)),
+ (['-target','i386-pc-win32'],(8,16,0,32,35,64)),
+ (['-target','msp430-none-none'],(2,14,0,32,35,48))]
+ for flags, values in tries:
+ align,total,a1,a2,a3,a4 = values
+
+ tu = get_tu(source, flags=flags)
+ teststruct = get_cursor(tu, 'a')
+ fields = list(teststruct.get_children())
+
+ assert teststruct.type.get_align() == align
+ assert teststruct.type.get_size() == total
+ assert teststruct.type.get_offset(fields[0].spelling) == a1
+ assert teststruct.type.get_offset(fields[1].spelling) == a2
+ assert teststruct.type.get_offset(fields[2].spelling) == a3
+ assert teststruct.type.get_offset(fields[3].spelling) == a4
+ assert fields[0].is_bitfield() == False
+ assert fields[1].is_bitfield() == True
+ assert fields[1].get_bitfield_width() == 3
+ assert fields[2].is_bitfield() == True
+ assert fields[2].get_bitfield_width() == 4
+ assert fields[3].is_bitfield() == False
+
+def test_offset():
+ """Ensure Cursor.get_record_field_offset works in anonymous records"""
+ source="""
+struct Test {
+ struct {
+ int bariton;
+ union {
+ int foo;
+ };
+ };
+ int bar;
+};"""
+ tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64)),
+ (['-target','nvptx64-unknown-unknown'],(8,24,0,32,64)),
+ (['-target','i386-pc-win32'],(8,16,0,32,64)),
+ (['-target','msp430-none-none'],(2,14,0,32,64))]
+ for flags, values in tries:
+ align,total,bariton,foo,bar = values
+ tu = get_tu(source)
+ teststruct = get_cursor(tu, 'Test')
+ fields = list(teststruct.get_children())
+ assert teststruct.type.get_offset("bariton") == bariton
+ assert teststruct.type.get_offset("foo") == foo
+ assert teststruct.type.get_offset("bar") == bar
+
+
diff --git a/bindings/python/tests/cindex/util.py b/bindings/python/tests/cindex/util.py
index 2323839..8614b02 100644
--- a/bindings/python/tests/cindex/util.py
+++ b/bindings/python/tests/cindex/util.py
@@ -3,7 +3,7 @@
from clang.cindex import Cursor
from clang.cindex import TranslationUnit
-def get_tu(source, lang='c', all_warnings=False):
+def get_tu(source, lang='c', all_warnings=False, flags=[]):
"""Obtain a translation unit from source and language.
By default, the translation unit is created from source file "t.<ext>"
@@ -14,8 +14,8 @@
all_warnings is a convenience argument to enable all compiler warnings.
"""
+ args = list(flags)
name = 't.c'
- args = []
if lang == 'cpp':
name = 't.cpp'
args.append('-std=c++11')
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index 787c44a..bd407a9 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -32,7 +32,7 @@
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
*/
#define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 15
+#define CINDEX_VERSION_MINOR 16
#define CINDEX_VERSION_ENCODE(major, minor) ( \
((major) * 10000) \
@@ -2901,6 +2901,83 @@
CINDEX_LINKAGE long long clang_getArraySize(CXType T);
/**
+ * \brief List the possible error codes for \c clang_Type_getSizeOf,
+ * \c clang_Type_getAlignOf, \c clang_Type_getOffsetOf and
+ * \c clang_Cursor_getOffsetOf.
+ *
+ * A value of this enumeration type can be returned if the target type is not
+ * a valid argument to sizeof, alignof or offsetof.
+ */
+enum CXTypeLayoutError {
+ /**
+ * \brief Type is of kind CXType_Invalid.
+ */
+ CXTypeLayoutError_Invalid = -1,
+ /**
+ * \brief The type is an incomplete Type.
+ */
+ CXTypeLayoutError_Incomplete = -2,
+ /**
+ * \brief The type is a dependent Type.
+ */
+ CXTypeLayoutError_Dependent = -3,
+ /**
+ * \brief The type is not a constant size type.
+ */
+ CXTypeLayoutError_NotConstantSize = -4,
+ /**
+ * \brief The Field name is not valid for this record.
+ */
+ CXTypeLayoutError_InvalidFieldName = -5
+};
+
+/**
+ * \brief Return the alignment of a type in bytes as per C++[expr.alignof]
+ * standard.
+ *
+ * If the type declaration is invalid, CXTypeLayoutError_Invalid is returned.
+ * If the type declaration is an incomplete type, CXTypeLayoutError_Incomplete
+ * is returned.
+ * If the type declaration is a dependent type, CXTypeLayoutError_Dependent is
+ * returned.
+ * If the type declaration is not a constant size type,
+ * CXTypeLayoutError_NotConstantSize is returned.
+ */
+CINDEX_LINKAGE long long clang_Type_getAlignOf(CXType T);
+
+/**
+ * \brief Return the size of a type in bytes as per C++[expr.sizeof] standard.
+ *
+ * If the type declaration is invalid, CXTypeLayoutError_Invalid is returned.
+ * If the type declaration is an incomplete type, CXTypeLayoutError_Incomplete
+ * is returned.
+ * If the type declaration is a dependent type, CXTypeLayoutError_Dependent is
+ * returned.
+ */
+CINDEX_LINKAGE long long clang_Type_getSizeOf(CXType T);
+
+/**
+ * \brief Return the offset of a field named S in a record of type T in bits
+ * as it would be returned by __offsetof__ as per C++11[18.2p4]
+ *
+ * If the cursor is not a record field declaration, CXTypeLayoutError_Invalid
+ * is returned.
+ * If the field's type declaration is an incomplete type,
+ * CXTypeLayoutError_Incomplete is returned.
+ * If the field's type declaration is a dependent type,
+ * CXTypeLayoutError_Dependent is returned.
+ * If the field's name S is not found,
+ * CXTypeLayoutError_InvalidFieldName is returned.
+ */
+CINDEX_LINKAGE long long clang_Type_getOffsetOf(CXType T, const char *S);
+
+/**
+ * \brief Returns non-zero if the cursor specifies a Record member that is a
+ * bitfield.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_isBitField(CXCursor C);
+
+/**
* \brief Returns 1 if the base class specified by the cursor with kind
* CX_CXXBaseSpecifier is virtual.
*/
diff --git a/test/Index/print-type-size.cpp b/test/Index/print-type-size.cpp
new file mode 100644
index 0000000..698d967
--- /dev/null
+++ b/test/Index/print-type-size.cpp
@@ -0,0 +1,428 @@
+// from SemaCXX/class-layout.cpp
+// RUN: c-index-test -test-print-type-size %s -target x86_64-pc-linux-gnu | FileCheck -check-prefix=CHECK64 %s
+// RUN: c-index-test -test-print-type-size %s -target i386-apple-darwin9 | FileCheck -check-prefix=CHECK32 %s
+
+namespace basic {
+
+// CHECK64: VarDecl=v:[[@LINE+2]]:6 (Definition) [type=void] [typekind=Void]
+// CHECK32: VarDecl=v:[[@LINE+1]]:6 (Definition) [type=void] [typekind=Void]
+void v;
+
+// CHECK64: VarDecl=v1:[[@LINE+2]]:7 (Definition) [type=void *] [typekind=Pointer] [sizeof=8] [alignof=8]
+// CHECK32: VarDecl=v1:[[@LINE+1]]:7 (Definition) [type=void *] [typekind=Pointer] [sizeof=4] [alignof=4]
+void *v1;
+
+// offsetof
+// CHECK64: StructDecl=simple:[[@LINE+2]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=48] [alignof=8]
+// CHECK32: StructDecl=simple:[[@LINE+1]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=36] [alignof=4]
+struct simple {
+ int a;
+ char b;
+// CHECK64: FieldDecl=c:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=40] [BitFieldSize=3]
+ int c:3;
+ long d;
+ int e:5;
+// CHECK64: FieldDecl=f:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=133] [BitFieldSize=4]
+ int f:4;
+// CHECK64: FieldDecl=g:[[@LINE+2]]:13 (Definition) [type=long long] [typekind=LongLong] [sizeof=8] [alignof=8] [offsetof=192]
+// CHECK32: FieldDecl=g:[[@LINE+1]]:13 (Definition) [type=long long] [typekind=LongLong] [sizeof=8] [alignof=4] [offsetof=128]
+ long long g;
+// CHECK64: FieldDecl=h:[[@LINE+1]]:8 (Definition) [type=char] [typekind=Char_S] [sizeof=1] [alignof=1] [offsetof=256] [BitFieldSize=3]
+ char h:3;
+ char i:3;
+ float j;
+// CHECK64: FieldDecl=k:[[@LINE+2]]:10 (Definition) [type=char *] [typekind=Pointer] [sizeof=8] [alignof=8] [offsetof=320]
+// CHECK32: FieldDecl=k:[[@LINE+1]]:10 (Definition) [type=char *] [typekind=Pointer] [sizeof=4] [alignof=4] [offsetof=256]
+ char * k;
+};
+
+
+// CHECK64: UnionDecl=u:[[@LINE+2]]:7 (Definition) [type=basic::u] [typekind=Record] [sizeof=48] [alignof=8]
+// CHECK32: UnionDecl=u:[[@LINE+1]]:7 (Definition) [type=basic::u] [typekind=Record] [sizeof=36] [alignof=4]
+union u {
+ int u1;
+ long long u2;
+ struct simple s1;
+};
+
+// CHECK64: VarDecl=s1:[[@LINE+2]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=48] [alignof=8]
+// CHECK32: VarDecl=s1:[[@LINE+1]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=36] [alignof=4]
+simple s1;
+
+struct Test {
+ struct {
+ union {
+//CHECK64: FieldDecl=foo:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0]
+ int foo;
+ };
+ };
+};
+
+struct Test2 {
+ struct {
+ struct {
+//CHECK64: FieldDecl=foo:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0]
+ int foo;
+ };
+ struct {
+//CHECK64: FieldDecl=bar:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=32]
+ int bar;
+ };
+ struct {
+ struct {
+//CHECK64: FieldDecl=foobar:[[@LINE+1]]:15 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64]
+ int foobar;
+ };
+ };
+ struct inner {
+ struct {
+//CHECK64: FieldDecl=mybar:[[@LINE+1]]:15 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0]
+ int mybar;
+ };
+//CHECK64: FieldDecl=mole:[[@LINE+1]]:7 (Definition) [type=struct inner] [typekind=Unexposed] [sizeof=4] [alignof=4] [offsetof=96]
+ } mole;
+ };
+};
+
+}
+
+// these are test crash. Offsetof return values are not important.
+namespace Incomplete {
+// test that fields in incomplete named record do not crash
+union named {
+ struct forward_decl f1;
+//CHECK64: FieldDecl=f2:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int f2;
+ struct x {
+//CHECK64: FieldDecl=g1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0]
+ int g1;
+//CHECK64: FieldDecl=f3:[[@LINE+1]]:5 (Definition) [type=struct x] [typekind=Unexposed] [sizeof=4] [alignof=4] [offsetof=-2]
+ } f3;
+ struct forward_decl f4;
+ struct x2{
+ int g2;
+ struct forward_decl g3;
+ } f5;
+};
+
+// test that fields in incomplete anonymous record do not crash
+union f {
+//CHECK64: FieldDecl=f1:[[@LINE+1]]:23 (Definition) [type=struct forward_decl] [typekind=Unexposed] [sizeof=-2] [alignof=-2] [offsetof=-2]
+ struct forward_decl f1;
+//CHECK64: FieldDecl=f2:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int f2;
+ struct {
+//CHECK64: FieldDecl=e1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int e1;
+ struct {
+//CHECK64: FieldDecl=g1:[[@LINE+1]]:28 (Definition) [type=struct forward_decl2] [typekind=Unexposed] [sizeof=-2] [alignof=-2] [offsetof=-2]
+ struct forward_decl2 g1;
+ };
+//CHECK64: FieldDecl=e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int e3;
+ };
+};
+
+
+// incomplete not in root level, in named record
+struct s1 {
+ struct {
+ struct forward_decl2 s1_g1;
+//CHECK64: FieldDecl=s1_e1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int s1_e1;
+ } s1_x; // named record shows in s1->field_iterator
+//CHECK64: FieldDecl=s1_e3:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int s1_e3;
+};
+
+// incomplete not in root level, in anonymous record
+struct s1b {
+ struct {
+ struct forward_decl2 s1b_g1;
+ }; // erroneous anonymous record does not show in s1b->field_iterator
+//CHECK64: FieldDecl=s1b_e2:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0]
+ int s1b_e2;
+};
+
+struct s2 {
+ struct {
+ struct forward_decl2 s2_g1;
+//CHECK64: FieldDecl=s2_e1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-5]
+ int s2_e1;
+ }; // erroneous anonymous record does not show in s1b->field_iterator
+//CHECK64: FieldDecl=s2_e3:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0]
+ int s2_e3;
+};
+
+//deep anonymous with deep level incomplete
+struct s3 {
+ struct {
+ int s3_e1;
+ struct {
+ struct {
+ struct {
+ struct {
+ struct forward_decl2 s3_g1;
+ };
+ };
+ };
+ };
+//CHECK64: FieldDecl=s3_e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64]
+ int s3_e3;
+ };
+};
+
+//deep anonymous with first level incomplete
+struct s4a {
+ struct forward_decl2 g1;
+ struct {
+ struct forward_decl2 g2;
+ struct {
+ struct {
+ struct {
+ struct {
+//CHECK64: FieldDecl=s4_e1:[[@LINE+1]]:17 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int s4_e1;
+ };
+ };
+ };
+ };
+//CHECK64: FieldDecl=s4_e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2]
+ int s4_e3;
+ };
+};
+
+//deep anonymous with sub-first-level incomplete
+struct s4b {
+ struct {
+ struct forward_decl2 g1;
+ struct {
+ struct {
+ struct {
+ struct {
+//CHECK64: FieldDecl=s4b_e1:[[@LINE+1]]:17 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-5]
+ int s4b_e1;
+ };
+ };
+ };
+ };
+//CHECK64: FieldDecl=s4b_e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-5]
+ int s4b_e3;
+ };
+};
+
+// CHECK64: StructDecl=As:[[@LINE+1]]:8 [type=Incomplete::As] [typekind=Record]
+struct As;
+
+// undefined class. Should not crash
+// CHECK64: ClassDecl=A:[[@LINE+1]]:7 [type=Incomplete::A] [typekind=Record]
+class A;
+// CHECK64: ClassDecl=B:[[@LINE+1]]:7 (Definition) [type=Incomplete::B] [typekind=Record] [sizeof=16] [alignof=8]
+class B {
+// CHECK64: FieldDecl=a1:[[@LINE+2]]:6 (Definition) [type=Incomplete::A *] [typekind=Pointer] [sizeof=8] [alignof=8] [offsetof=0]
+// CHECK32: FieldDecl=a1:[[@LINE+1]]:6 (Definition) [type=Incomplete::A *] [typekind=Pointer] [sizeof=4] [alignof=4] [offsetof=0]
+ A* a1;
+// CHECK64: FieldDecl=a2:[[@LINE+2]]:6 (Definition) [type=Incomplete::A &] [typekind=LValueReference] [sizeof=-2] [alignof=-2] [offsetof=64]
+// CHECK32: FieldDecl=a2:[[@LINE+1]]:6 (Definition) [type=Incomplete::A &] [typekind=LValueReference] [sizeof=-2] [alignof=-2] [offsetof=32]
+ A& a2;
+};
+
+}
+
+namespace Sizes {
+
+// CHECK64: StructDecl=A:[[@LINE+2]]:8 (Definition) [type=Sizes::A] [typekind=Record] [sizeof=8] [alignof=4]
+// CHECK32: StructDecl=A:[[@LINE+1]]:8 (Definition) [type=Sizes::A] [typekind=Record] [sizeof=8] [alignof=4]
+struct A {
+ int a;
+ char b;
+};
+
+// CHECK64: StructDecl=B:[[@LINE+2]]:8 (Definition) [type=Sizes::B] [typekind=Record] [sizeof=12] [alignof=4]
+// CHECK32: StructDecl=B:[[@LINE+1]]:8 (Definition) [type=Sizes::B] [typekind=Record] [sizeof=12] [alignof=4]
+struct B : A {
+ char c;
+};
+
+// CHECK64: StructDecl=C:[[@LINE+2]]:8 (Definition) [type=Sizes::C] [typekind=Record] [sizeof=8] [alignof=4]
+// CHECK32: StructDecl=C:[[@LINE+1]]:8 (Definition) [type=Sizes::C] [typekind=Record] [sizeof=8] [alignof=4]
+struct C {
+// Make fields private so C won't be a POD type.
+private:
+ int a;
+ char b;
+};
+
+// CHECK64: StructDecl=D:[[@LINE+2]]:8 (Definition) [type=Sizes::D] [typekind=Record] [sizeof=8] [alignof=4]
+// CHECK32: StructDecl=D:[[@LINE+1]]:8 (Definition) [type=Sizes::D] [typekind=Record] [sizeof=8] [alignof=4]
+struct D : C {
+ char c;
+};
+
+// CHECK64: StructDecl=E:[[@LINE+2]]:32 (Definition) [type=Sizes::E] [typekind=Record] [sizeof=5] [alignof=1]
+// CHECK32: StructDecl=E:[[@LINE+1]]:32 (Definition) [type=Sizes::E] [typekind=Record] [sizeof=5] [alignof=1]
+struct __attribute__((packed)) E {
+ char b;
+ int a;
+};
+
+// CHECK64: StructDecl=F:[[@LINE+2]]:32 (Definition) [type=Sizes::F] [typekind=Record] [sizeof=6] [alignof=1]
+// CHECK32: StructDecl=F:[[@LINE+1]]:32 (Definition) [type=Sizes::F] [typekind=Record] [sizeof=6] [alignof=1]
+struct __attribute__((packed)) F : E {
+ char d;
+};
+
+struct G { G(); };
+// CHECK64: StructDecl=H:[[@LINE+2]]:8 (Definition) [type=Sizes::H] [typekind=Record] [sizeof=1] [alignof=1]
+// CHECK32: StructDecl=H:[[@LINE+1]]:8 (Definition) [type=Sizes::H] [typekind=Record] [sizeof=1] [alignof=1]
+struct H : G { };
+
+// CHECK64: StructDecl=I:[[@LINE+2]]:8 (Definition) [type=Sizes::I] [typekind=Record] [sizeof=5] [alignof=1]
+// CHECK32: StructDecl=I:[[@LINE+1]]:8 (Definition) [type=Sizes::I] [typekind=Record] [sizeof=5] [alignof=1]
+struct I {
+ char b;
+ int a;
+} __attribute__((packed));
+
+}
+
+namespace Test1 {
+
+// Test complex class hierarchy
+struct A { };
+struct B : A { virtual void b(); };
+class C : virtual A { int c; };
+struct D : virtual B { };
+struct E : C, virtual D { };
+class F : virtual E { };
+// CHECK64: StructDecl=G:[[@LINE+2]]:8 (Definition) [type=Test1::G] [typekind=Record] [sizeof=24] [alignof=8]
+// CHECK32: StructDecl=G:[[@LINE+1]]:8 (Definition) [type=Test1::G] [typekind=Record] [sizeof=16] [alignof=4]
+struct G : virtual E, F { };
+
+}
+
+namespace Test2 {
+
+// Test that this somewhat complex class structure is laid out correctly.
+struct A { };
+struct B : A { virtual void b(); };
+struct C : virtual B { };
+struct D : virtual A { };
+struct E : virtual B, D { };
+struct F : E, virtual C { };
+struct G : virtual F, A { };
+// CHECK64: StructDecl=H:[[@LINE+2]]:8 (Definition) [type=Test2::H] [typekind=Record] [sizeof=24] [alignof=8]
+// CHECK32: StructDecl=H:[[@LINE+1]]:8 (Definition) [type=Test2::H] [typekind=Record] [sizeof=12] [alignof=4]
+struct H { G g; };
+
+}
+
+namespace Test3 {
+// CHECK64: ClassDecl=B:[[@LINE+2]]:7 (Definition) [type=Test3::B] [typekind=Record] [sizeof=16] [alignof=8]
+// CHECK32: ClassDecl=B:[[@LINE+1]]:7 (Definition) [type=Test3::B] [typekind=Record] [sizeof=8] [alignof=4]
+class B {
+public:
+ virtual void b(){}
+// CHECK64: FieldDecl=b_field:[[@LINE+2]]:8 (Definition) [type=long] [typekind=Long] [sizeof=8] [alignof=8] [offsetof=64]
+// CHECK32: FieldDecl=b_field:[[@LINE+1]]:8 (Definition) [type=long] [typekind=Long] [sizeof=4] [alignof=4] [offsetof=32]
+ long b_field;
+protected:
+private:
+};
+
+// CHECK32: ClassDecl=A:[[@LINE+1]]:7 (Definition) [type=Test3::A] [typekind=Record] [sizeof=16] [alignof=4]
+class A : public B {
+public:
+// CHECK64: FieldDecl=a_field:[[@LINE+2]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=128]
+// CHECK32: FieldDecl=a_field:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64]
+ int a_field;
+ virtual void a(){}
+// CHECK64: FieldDecl=one:[[@LINE+2]]:8 (Definition) [type=char] [typekind=Char_S] [sizeof=1] [alignof=1] [offsetof=160]
+// CHECK32: FieldDecl=one:[[@LINE+1]]:8 (Definition) [type=char] [typekind=Char_S] [sizeof=1] [alignof=1] [offsetof=96]
+ char one;
+protected:
+private:
+};
+
+// CHECK64: ClassDecl=D:[[@LINE+2]]:7 (Definition) [type=Test3::D] [typekind=Record] [sizeof=16] [alignof=8]
+// CHECK32: ClassDecl=D:[[@LINE+1]]:7 (Definition) [type=Test3::D] [typekind=Record] [sizeof=12] [alignof=4]
+class D {
+public:
+ virtual void b(){}
+// CHECK64: FieldDecl=a:[[@LINE+2]]:10 (Definition) [type=double] [typekind=Double] [sizeof=8] [alignof=8] [offsetof=64]
+// CHECK32: FieldDecl=a:[[@LINE+1]]:10 (Definition) [type=double] [typekind=Double] [sizeof=8] [alignof=4] [offsetof=32]
+ double a;
+};
+
+// CHECK64: ClassDecl=C:[[@LINE+2]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=88] [alignof=8]
+// CHECK32: ClassDecl=C:[[@LINE+1]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=60] [alignof=4]
+class C : public virtual A,
+ public D, public B {
+public:
+ double c1_field;
+ int c2_field;
+ double c3_field;
+ int c4_field;
+ virtual void foo(){}
+ virtual void bar(){}
+protected:
+private:
+};
+
+struct BaseStruct
+{
+ BaseStruct(){}
+ double v0;
+ float v1;
+// CHECK64: FieldDecl=fg:[[@LINE+2]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=88] [alignof=8] [offsetof=128]
+// CHECK32: FieldDecl=fg:[[@LINE+1]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=60] [alignof=4] [offsetof=96]
+ C fg;
+// CHECK64: FieldDecl=rg:[[@LINE+2]]:8 (Definition) [type=Test3::C &] [typekind=LValueReference] [sizeof=88] [alignof=8] [offsetof=832]
+// CHECK32: FieldDecl=rg:[[@LINE+1]]:8 (Definition) [type=Test3::C &] [typekind=LValueReference] [sizeof=60] [alignof=4] [offsetof=576]
+ C &rg;
+ int x;
+};
+
+}
+
+namespace NotConstantSize {
+
+void f(int i) {
+// CHECK32: VarDecl=v2:[[@LINE+1]]:8 (Definition) [type=int [i]] [typekind=Unexposed] [sizeof=-4] [alignof=4]
+ int v2[i];
+ {
+ struct CS1 {
+// FIXME: should libclang return [offsetof=0] ?
+//CHECK32: FieldDecl=f1:[[@LINE+1]]:9 (Definition) [type=int [i]] [typekind=Unexposed] [sizeof=-4] [alignof=4] [offsetof=0]
+ int f1[i];
+//CHECK32: FieldDecl=f2:[[@LINE+1]]:11 (Definition) [type=float] [typekind=Float] [sizeof=4] [alignof=4] [offsetof=0]
+ float f2;
+ };
+ }
+}
+
+}
+
+namespace CrashTest {
+// test crash scenarios on dependent types.
+template<typename T>
+struct Foo {
+//CHECK32: FieldDecl=t:[[@LINE+1]]:5 (Definition) [type=T] [typekind=Unexposed] [sizeof=-3] [alignof=-3] [offsetof=-1]
+ T t;
+//CHECK32: FieldDecl=a:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-1]
+ int a;
+};
+
+Foo<Sizes::A> t1;
+Foo<Sizes::I> t2;
+
+void c;
+
+plopplop;
+
+// CHECK64: StructDecl=lastValid:[[@LINE+2]]:8 (Definition) [type=CrashTest::lastValid] [typekind=Record] [sizeof=1] [alignof=1]
+// CHECK32: StructDecl=lastValid:[[@LINE+1]]:8 (Definition) [type=CrashTest::lastValid] [typekind=Record] [sizeof=1] [alignof=1]
+struct lastValid {
+};
+
+}
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index ce9e491..e97cb30 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1144,6 +1144,61 @@
return CXChildVisit_Recurse;
}
+static enum CXChildVisitResult PrintTypeSize(CXCursor cursor, CXCursor p,
+ CXClientData d) {
+ CXType T;
+ enum CXCursorKind K = clang_getCursorKind(cursor);
+ if (clang_isInvalid(K))
+ return CXChildVisit_Recurse;
+ T = clang_getCursorType(cursor);
+ PrintCursor(cursor, NULL);
+ PrintTypeAndTypeKind(T, " [type=%s] [typekind=%s]");
+ /* Print the type sizeof if applicable. */
+ {
+ long long Size = clang_Type_getSizeOf(T);
+ if (Size >= 0 || Size < -1 ) {
+ printf(" [sizeof=%lld]", Size);
+ }
+ }
+ /* Print the type alignof if applicable. */
+ {
+ long long Align = clang_Type_getAlignOf(T);
+ if (Align >= 0 || Align < -1) {
+ printf(" [alignof=%lld]", Align);
+ }
+ }
+ /* Print the record field offset if applicable. */
+ {
+ const char *FieldName = clang_getCString(clang_getCursorSpelling(cursor));
+ /* recurse to get the root anonymous record parent */
+ CXCursor Parent, Root;
+ if (clang_getCursorKind(cursor) == CXCursor_FieldDecl ) {
+ const char *RootParentName;
+ Root = Parent = p;
+ do {
+ Root = Parent;
+ RootParentName = clang_getCString(clang_getCursorSpelling(Root));
+ Parent = clang_getCursorSemanticParent(Root);
+ } while ( clang_getCursorType(Parent).kind == CXType_Record &&
+ !strcmp(RootParentName, "") );
+ /* if RootParentName is "", record is anonymous. */
+ {
+ long long Offset = clang_Type_getOffsetOf(clang_getCursorType(Root),
+ FieldName);
+ printf(" [offsetof=%lld]", Offset);
+ }
+ }
+ }
+ /* Print if its a bitfield */
+ {
+ int IsBitfield = clang_Cursor_isBitField(cursor);
+ if (IsBitfield)
+ printf(" [BitFieldSize=%d]", clang_getFieldDeclBitWidth(cursor));
+ }
+ printf("\n");
+ return CXChildVisit_Recurse;
+}
+
/******************************************************************************/
/* Bitwidth testing. */
/******************************************************************************/
@@ -3642,6 +3697,7 @@
fprintf(stderr,
" c-index-test -test-print-linkage-source {<args>}*\n"
" c-index-test -test-print-type {<args>}*\n"
+ " c-index-test -test-print-type-size {<args>}*\n"
" c-index-test -test-print-bitwidth {<args>}*\n"
" c-index-test -print-usr [<CursorKind> {<args>}]*\n"
" c-index-test -print-usr-file <file>\n"
@@ -3728,6 +3784,9 @@
else if (argc > 2 && strcmp(argv[1], "-test-print-type") == 0)
return perform_test_load_source(argc - 2, argv + 2, "all",
PrintType, 0);
+ else if (argc > 2 && strcmp(argv[1], "-test-print-type-size") == 0)
+ return perform_test_load_source(argc - 2, argv + 2, "all",
+ PrintTypeSize, 0);
else if (argc > 2 && strcmp(argv[1], "-test-print-bitwidth") == 0)
return perform_test_load_source(argc - 2, argv + 2, "all",
PrintBitWidth, 0);
diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp
index 6f87fc5..16009bf 100644
--- a/tools/libclang/CXType.cpp
+++ b/tools/libclang/CXType.cpp
@@ -651,6 +651,126 @@
return result;
}
+long long clang_Type_getAlignOf(CXType T) {
+ if (T.kind == CXType_Invalid)
+ return CXTypeLayoutError_Invalid;
+ ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext();
+ QualType QT = GetQualType(T);
+ // [expr.alignof] p1: return size_t value for complete object type, reference
+ // or array.
+ // [expr.alignof] p3: if reference type, return size of referenced type
+ if (QT->isReferenceType())
+ QT = QT.getNonReferenceType();
+ if (QT->isIncompleteType())
+ return CXTypeLayoutError_Incomplete;
+ if (QT->isDependentType())
+ return CXTypeLayoutError_Dependent;
+ // Exceptions by GCC extension - see ASTContext.cpp:1313 getTypeInfoImpl
+ // if (QT->isFunctionType()) return 4; // Bug #15511 - should be 1
+ // if (QT->isVoidType()) return 1;
+ return Ctx.getTypeAlignInChars(QT).getQuantity();
+}
+
+long long clang_Type_getSizeOf(CXType T) {
+ if (T.kind == CXType_Invalid)
+ return CXTypeLayoutError_Invalid;
+ ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext();
+ QualType QT = GetQualType(T);
+ // [expr.sizeof] p2: if reference type, return size of referenced type
+ if (QT->isReferenceType())
+ QT = QT.getNonReferenceType();
+ // [expr.sizeof] p1: return -1 on: func, incomplete, bitfield, incomplete
+ // enumeration
+ // Note: We get the cxtype, not the cxcursor, so we can't call
+ // FieldDecl->isBitField()
+ // [expr.sizeof] p3: pointer ok, function not ok.
+ // [gcc extension] lib/AST/ExprConstant.cpp:1372 HandleSizeof : vla == error
+ if (QT->isIncompleteType())
+ return CXTypeLayoutError_Incomplete;
+ if (QT->isDependentType())
+ return CXTypeLayoutError_Dependent;
+ if (!QT->isConstantSizeType())
+ return CXTypeLayoutError_NotConstantSize;
+ // [gcc extension] lib/AST/ExprConstant.cpp:1372
+ // HandleSizeof : {voidtype,functype} == 1
+ // not handled by ASTContext.cpp:1313 getTypeInfoImpl
+ if (QT->isVoidType() || QT->isFunctionType())
+ return 1;
+ return Ctx.getTypeSizeInChars(QT).getQuantity();
+}
+
+static long long visitRecordForValidation(const RecordDecl *RD) {
+ for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
+ I != E; ++I){
+ QualType FQT = (*I)->getType();
+ if (FQT->isIncompleteType())
+ return CXTypeLayoutError_Incomplete;
+ if (FQT->isDependentType())
+ return CXTypeLayoutError_Dependent;
+ // recurse
+ if (const RecordType *ChildType = (*I)->getType()->getAs<RecordType>()) {
+ if (const RecordDecl *Child = ChildType->getDecl()) {
+ long long ret = visitRecordForValidation(Child);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ // else try next field
+ }
+ return 0;
+}
+
+long long clang_Type_getOffsetOf(CXType PT, const char *S) {
+ // check that PT is not incomplete/dependent
+ CXCursor PC = clang_getTypeDeclaration(PT);
+ if (clang_isInvalid(PC.kind))
+ return CXTypeLayoutError_Invalid;
+ const RecordDecl *RD =
+ dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC));
+ if (!RD)
+ return CXTypeLayoutError_Invalid;
+ RD = RD->getDefinition();
+ if (!RD)
+ return CXTypeLayoutError_Incomplete;
+ QualType RT = GetQualType(PT);
+ if (RT->isIncompleteType())
+ return CXTypeLayoutError_Incomplete;
+ if (RT->isDependentType())
+ return CXTypeLayoutError_Dependent;
+ // We recurse into all record fields to detect incomplete and dependent types.
+ long long Error = visitRecordForValidation(RD);
+ if (Error < 0)
+ return Error;
+ if (!S)
+ return CXTypeLayoutError_InvalidFieldName;
+ // lookup field
+ ASTContext &Ctx = cxtu::getASTUnit(GetTU(PT))->getASTContext();
+ IdentifierInfo *II = &Ctx.Idents.get(S);
+ DeclarationName FieldName(II);
+ RecordDecl::lookup_const_result Res = RD->lookup(FieldName);
+ // If a field of the parent record is incomplete, lookup will fail.
+ // and we would return InvalidFieldName instead of Incomplete.
+ // But this erroneous results does protects again a hidden assertion failure
+ // in the RecordLayoutBuilder
+ if (Res.size() != 1)
+ return CXTypeLayoutError_InvalidFieldName;
+ if (const FieldDecl *FD = dyn_cast<FieldDecl>(Res.front()))
+ return Ctx.getFieldOffset(FD);
+ if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(Res.front()))
+ return Ctx.getFieldOffset(IFD);
+ // we don't want any other Decl Type.
+ return CXTypeLayoutError_InvalidFieldName;
+}
+
+unsigned clang_Cursor_isBitField(CXCursor C) {
+ if (!clang_isDeclaration(C.kind))
+ return 0;
+ const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(cxcursor::getCursorDecl(C));
+ if (!FD)
+ return 0;
+ return FD->isBitField();
+}
+
CXString clang_getDeclObjCTypeEncoding(CXCursor C) {
if (!clang_isDeclaration(C.kind))
return cxstring::createEmpty();
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index d99f24e..1f179f1 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -14,6 +14,7 @@
clang_Cursor_getSpellingNameRange
clang_Cursor_getTranslationUnit
clang_Cursor_getReceiverType
+clang_Cursor_isBitField
clang_Cursor_isDynamicCall
clang_Cursor_isNull
clang_Cursor_getModule
@@ -53,6 +54,9 @@
clang_TParamCommandComment_isParamPositionValid
clang_TParamCommandComment_getDepth
clang_TParamCommandComment_getIndex
+clang_Type_getAlignOf
+clang_Type_getSizeOf
+clang_Type_getOffsetOf
clang_VerbatimBlockLineComment_getText
clang_VerbatimLineComment_getText
clang_HTMLTagComment_getAsString