From 411d33aa0b0d3bc9b2faec40cd821bdd836094ab Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <akyrtzi@gmail.com> Date: Thu, 11 Apr 2013 01:20:11 +0000 Subject: [PATCH] [libclang] Expose record layout info via new libclang functions: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- bindings/python/clang/cindex.py | 50 +++ bindings/python/tests/cindex/test_type.py | 63 ++++ bindings/python/tests/cindex/util.py | 4 +- include/clang-c/Index.h | 79 +++- test/Index/print-type-size.cpp | 428 ++++++++++++++++++++++ tools/c-index-test/c-index-test.c | 59 +++ tools/libclang/CXType.cpp | 120 ++++++ tools/libclang/libclang.exports | 4 + 8 files changed, 804 insertions(+), 3 deletions(-) create mode 100644 test/Index/print-type-size.cpp diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py index 70f4f36a2cf..36761bd8596 100644 --- a/bindings/python/clang/cindex.py +++ b/bindings/python/clang/cindex.py @@ -1314,6 +1314,18 @@ class Cursor(Structure): """ 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 @@ class Type(Structure): """ 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 @@ functionList = [ [Type], c_longlong), + ("clang_getFieldDeclBitWidth", + [Cursor], + c_int), + ("clang_getCanonicalCursor", [Cursor], Cursor, @@ -3038,6 +3072,22 @@ functionList = [ [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 28e4411c779..9bbed5aa940 100644 --- a/bindings/python/tests/cindex/test_type.py +++ b/bindings/python/tests/cindex/test_type.py @@ -298,3 +298,66 @@ def test_is_restrict_qualified(): 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 2323839306d..8614b02ad25 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 @@ def get_tu(source, lang='c', all_warnings=False): 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 787c44a2e4f..bd407a997d3 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) \ @@ -2900,6 +2900,83 @@ CINDEX_LINKAGE CXType clang_getArrayElementType(CXType T); */ 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 00000000000..698d96705bc --- /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 ce9e4910ae0..e97cb30589b 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -1144,6 +1144,61 @@ static enum CXChildVisitResult PrintType(CXCursor cursor, CXCursor p, 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 @@ static void print_usage(void) { 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 @@ int cindextest_main(int argc, const char **argv) { 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 6f87fc51a41..16009bf47af 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -651,6 +651,126 @@ long long clang_getArraySize(CXType CT) { 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 d99f24ef038..1f179f13bd8 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -14,6 +14,7 @@ clang_Cursor_getObjCSelectorIndex 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_getParamName 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 -- GitLab