From 77cb1fcda28965de518051d3db752b2dbbe4bf4a Mon Sep 17 00:00:00 2001 From: Vitaly Cheptsov <4348897+vit9696@users.noreply.github.com> Date: Mon, 20 Sep 2021 16:18:41 +0300 Subject: Improve _Atomic support and add more tests (#431) * Improve _Atomic support with more tests and fix typedef handling * Remove duplicated tests and check the generated code for typedefs * Add typedef testing to parser as well Co-authored-by: vit9696 --- pycparser/c_parser.py | 14 +++++-- tests/c_files/c11.c | 69 ++++++++++++++++++-------------- tests/test_c_generator.py | 22 ++++++++++ tests/test_c_parser.py | 14 +++++++ utils/fake_libc_include/_fake_defines.h | 15 +++++++ utils/fake_libc_include/_fake_typedefs.h | 48 ++++++++++++++++++++++ utils/fake_libc_include/stdatomic.h | 2 + 7 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 utils/fake_libc_include/stdatomic.h diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py index ad7ae50..0f026eb 100644 --- a/pycparser/c_parser.py +++ b/pycparser/c_parser.py @@ -779,6 +779,13 @@ class CParser(PLYParser): """ p[0] = self._add_declaration_specifier(p[2], p[1], 'function') + # This is a bit ugly, but we need to process atomic specifier before qualifiers + # for _Atomic(x) in typedefs. + def p_declaration_specifiers_no_type_4(self, p): + """ declaration_specifiers_no_type : atomic_specifier declaration_specifiers_no_type_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'type') + def p_declaration_specifiers_1(self, p): """ declaration_specifiers : declaration_specifiers type_qualifier """ @@ -841,17 +848,18 @@ class CParser(PLYParser): """ p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1)) - def p_type_specifier_1(self, p): + def p_type_specifier(self, p): """ type_specifier : typedef_name | enum_specifier | struct_or_union_specifier | type_specifier_no_typeid + | atomic_specifier """ p[0] = p[1] # See section 6.7.2.4 of the C11 standard. - def p_type_specifier_2(self, p): - """ type_specifier : _ATOMIC LPAREN type_name RPAREN + def p_atomic_specifier(self, p): + """ atomic_specifier : _ATOMIC LPAREN type_name RPAREN """ typ = p[3] typ.quals.append('_Atomic') diff --git a/tests/c_files/c11.c b/tests/c_files/c11.c index 854feba..3c57f55 100644 --- a/tests/c_files/c11.c +++ b/tests/c_files/c11.c @@ -1,31 +1,38 @@ -#include -#include -#include -#include -#include - -/* C11 thread locals */ -_Thread_local int flag; -thread_local int flag2; - -static_assert(sizeof(flag) == sizeof(flag2), "Really unexpected size difference"); - -noreturn void func2(void) -{ - abort(); -} - -_Noreturn void func(void) -{ - func2(); -} - -int main() -{ - _Static_assert(sizeof(flag) == sizeof(flag2), "Unexpected size difference"); - static_assert(sizeof(flag) == sizeof(flag2), "Unexpected size difference"); - - printf("Flag: %d\n", flag); - printf("Flag2: %d\n", flag2); - func(); -} +#include +#include +#include +#include +#include +#include + +/* C11 thread locals */ +_Thread_local int flag; +thread_local int flag2; +_Atomic int flag3; +_Atomic(int) flag4; +_Atomic(_Atomic(int) *) flag5; +atomic_bool flag6; + +static_assert(sizeof(flag) == sizeof(flag2), "Really unexpected size difference"); + +noreturn void func2(void) +{ + abort(); +} + +_Noreturn void func(void) +{ + func2(); +} + +int main() +{ + _Static_assert(sizeof(flag) == sizeof(flag2), "Unexpected size difference"); + static_assert(sizeof(flag) == sizeof(flag2), "Unexpected size difference"); + static_assert(sizeof(flag) == sizeof(flag3), "Unexpected size difference"); + static_assert(sizeof(flag) == sizeof(flag4), "Unexpected size difference"); + + printf("Flag: %d\n", flag); + printf("Flag2: %d\n", flag2); + func(); +} diff --git a/tests/test_c_generator.py b/tests/test_c_generator.py index 0926b15..bcbdfcc 100644 --- a/tests/test_c_generator.py +++ b/tests/test_c_generator.py @@ -392,6 +392,28 @@ class TestCtoC(unittest.TestCase): self.assertEqual(c3, '_Atomic int * _Atomic x;\n') self._assert_ctoc_correct(s3) + # FIXME: Regeneration with multiple qualifiers is not fully supported. + # REF: https://github.com/eliben/pycparser/issues/433 + # self._assert_ctoc_correct('auto const _Atomic(int *) a;') + + s4 = 'typedef _Atomic(int) atomic_int;' + c4 = self._run_c_to_c(s4) + self.assertEqual(c4, 'typedef _Atomic int atomic_int;\n') + self._assert_ctoc_correct(s4) + + s5 = 'typedef _Atomic(_Atomic(_Atomic(int (*)(void)) *) *) t;' + c5 = self._run_c_to_c(s5) + self.assertEqual(c5, 'typedef int (* _Atomic * _Atomic * _Atomic t)(void);\n') + self._assert_ctoc_correct(s5) + + self._assert_ctoc_correct(r''' + typedef struct node_t { + _Atomic(void*) a; + _Atomic(void) *b; + _Atomic void *c; + } node; + ''') + def test_nested_sizeof(self): src = '1' for _ in range(30): diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py index 40b53af..7e6a648 100755 --- a/tests/test_c_parser.py +++ b/tests/test_c_parser.py @@ -572,6 +572,20 @@ class TestCParser_fundamentals(TestCParser_base): 'bar', ['TypeDecl', ['IdentifierType', ['int']]]]) + # typedefs with _Atomic specifiers. + s = 'typedef _Atomic(int) atomic_int;' + self.assertEqual(self.get_decl(s, 0), + ['Typedef', 'atomic_int', ['TypeDecl', ['IdentifierType', ['int']]]]) + + s = 'typedef _Atomic(_Atomic(_Atomic(int (*)(void)) *) *) t;' + self.assertEqual(self.get_decl(s, 0), + ['Typedef', 't', + ['PtrDecl', ['_Atomic'], + ['PtrDecl', ['_Atomic'], + ['PtrDecl', ['_Atomic'], + ['FuncDecl', [['Typename', ['TypeDecl', ['IdentifierType', ['void']]]]], + ['TypeDecl', ['IdentifierType', ['int']]]]]]]]) + def test_sizeof(self): e = """ void foo() diff --git a/utils/fake_libc_include/_fake_defines.h b/utils/fake_libc_include/_fake_defines.h index f2e8dd4..86b5c66 100644 --- a/utils/fake_libc_include/_fake_defines.h +++ b/utils/fake_libc_include/_fake_defines.h @@ -236,4 +236,19 @@ /* C11 assert.h defines */ #define static_assert _Static_assert +/* C11 stdatomic.h defines */ +#define ATOMIC_BOOL_LOCK_FREE 0 +#define ATOMIC_CHAR_LOCK_FREE 0 +#define ATOMIC_CHAR16_T_LOCK_FREE 0 +#define ATOMIC_CHAR32_T_LOCK_FREE 0 +#define ATOMIC_WCHAR_T_LOCK_FREE 0 +#define ATOMIC_SHORT_LOCK_FREE 0 +#define ATOMIC_INT_LOCK_FREE 0 +#define ATOMIC_LONG_LOCK_FREE 0 +#define ATOMIC_LLONG_LOCK_FREE 0 +#define ATOMIC_POINTER_LOCK_FREE 0 +#define ATOMIC_VAR_INIT(value) (value) +#define ATOMIC_FLAG_INIT { 0 } +#define kill_dependency(y) (y) + #endif diff --git a/utils/fake_libc_include/_fake_typedefs.h b/utils/fake_libc_include/_fake_typedefs.h index dfcc653..9a85d40 100644 --- a/utils/fake_libc_include/_fake_typedefs.h +++ b/utils/fake_libc_include/_fake_typedefs.h @@ -169,4 +169,52 @@ typedef struct xcb_connection_t xcb_connection_t; typedef uint32_t xcb_window_t; typedef uint32_t xcb_visualid_t; +/* C11 stdatomic.h types */ +typedef _Atomic(_Bool) atomic_bool; +typedef _Atomic(char) atomic_char; +typedef _Atomic(signed char) atomic_schar; +typedef _Atomic(unsigned char) atomic_uchar; +typedef _Atomic(short) atomic_short; +typedef _Atomic(unsigned short) atomic_ushort; +typedef _Atomic(int) atomic_int; +typedef _Atomic(unsigned int) atomic_uint; +typedef _Atomic(long) atomic_long; +typedef _Atomic(unsigned long) atomic_ulong; +typedef _Atomic(long long) atomic_llong; +typedef _Atomic(unsigned long long) atomic_ullong; +typedef _Atomic(uint_least16_t) atomic_char16_t; +typedef _Atomic(uint_least32_t) atomic_char32_t; +typedef _Atomic(wchar_t) atomic_wchar_t; +typedef _Atomic(int_least8_t) atomic_int_least8_t; +typedef _Atomic(uint_least8_t) atomic_uint_least8_t; +typedef _Atomic(int_least16_t) atomic_int_least16_t; +typedef _Atomic(uint_least16_t) atomic_uint_least16_t; +typedef _Atomic(int_least32_t) atomic_int_least32_t; +typedef _Atomic(uint_least32_t) atomic_uint_least32_t; +typedef _Atomic(int_least64_t) atomic_int_least64_t; +typedef _Atomic(uint_least64_t) atomic_uint_least64_t; +typedef _Atomic(int_fast8_t) atomic_int_fast8_t; +typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t; +typedef _Atomic(int_fast16_t) atomic_int_fast16_t; +typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t; +typedef _Atomic(int_fast32_t) atomic_int_fast32_t; +typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t; +typedef _Atomic(int_fast64_t) atomic_int_fast64_t; +typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t; +typedef _Atomic(intptr_t) atomic_intptr_t; +typedef _Atomic(uintptr_t) atomic_uintptr_t; +typedef _Atomic(size_t) atomic_size_t; +typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; +typedef _Atomic(intmax_t) atomic_intmax_t; +typedef _Atomic(uintmax_t) atomic_uintmax_t; +typedef struct atomic_flag { atomic_bool _Value; } atomic_flag; +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + #endif diff --git a/utils/fake_libc_include/stdatomic.h b/utils/fake_libc_include/stdatomic.h new file mode 100644 index 0000000..f952c1d --- /dev/null +++ b/utils/fake_libc_include/stdatomic.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" -- cgit v1.2.1