From f73b1d68913b9c3aad9a66d6b8aa75d614ec0dd7 Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev Date: Thu, 12 Aug 2021 17:12:40 +0300 Subject: [PATCH] Abandon standard C conformance and require GNU extensions Removed the use_gnu_ext option as well as fallback paths for compilers that don't support GNU extensions. To my knowledge, none of those compilers support C11 to a sufficient extent to compile Taisei anyway, and those fallbacks are very poorly tested. Pedantic warnings are now disabled, and extensions that are common to reasonably recent versions of GCC and clang are permitted to be relied on (list of allowed extensions TBA). --- meson.build | 5 -- meson_options.txt | 7 -- src/dynarray.h | 135 +++++++++++++--------------- src/entity.h | 14 ++- src/eventloop/executor_emscripten.c | 4 +- src/list.h | 60 ++++++------- src/util/assert.c | 4 +- src/util/compat.h | 41 +++------ src/util/miscmath.c | 2 +- src/vfs/sync_emscripten.c | 4 +- 10 files changed, 112 insertions(+), 164 deletions(-) diff --git a/meson.build b/meson.build index a7e536ac..451b9dd3 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,6 @@ project('taisei', 'c', version : 'v1.4-dev', meson_version : '>=0.53.0', default_options : [ - # Should really be c11, but gnu11 is a safer default because everything is terrible. 'c_std=gnu11', 'default_library=static', @@ -60,7 +59,6 @@ config = configuration_data() taisei_c_args = [ '-Wall', - '-Wpedantic', '-Werror=assume', '-Werror=implicit-function-declaration', @@ -81,10 +79,8 @@ taisei_c_args = [ '-Wfloat-overflow-conversion', '-Wfloat-zero-conversion', '-Wfor-loop-analysis', - '-Wformat-pedantic', '-Wformat-security', '-Wgcc-compat', - '-Wgnu', '-Wignored-qualifiers', '-Winit-self', '-Wlogical-op', @@ -272,7 +268,6 @@ else config.set('TAISEI_BUILDCONF_HAVE_MAX_ALIGN_T', malloc_alignment > 0) endif -config.set('TAISEI_BUILDCONF_USE_GNU_EXTENSIONS', get_option('use_gnu_ext')) config.set('TAISEI_BUILDCONF_HAVE_BUILTIN_POPCOUNTLL', cc.has_function('__builtin_popcountll')) config.set('TAISEI_BUILDCONF_HAVE_BUILTIN_POPCOUNT', cc.has_function('__builtin_popcount')) config.set('TAISEI_BUILDCONF_HAVE_BUILTIN_AVAILABLE', cc.has_function('__builtin_available')) diff --git a/meson_options.txt b/meson_options.txt index db3d41ba..f66390c8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -190,10 +190,3 @@ option( value : false, description : 'Build shaderc and spirv-cross CLI tools from subprojects even if system versions exist' ) - -option( - 'use_gnu_ext', - type : 'boolean', - value : true, - description : 'Allow use of some GNU C extensions (if supported by compiler)' -) diff --git a/src/dynarray.h b/src/dynarray.h index 7d2ff75c..57dc5f09 100644 --- a/src/dynarray.h +++ b/src/dynarray.h @@ -33,43 +33,26 @@ typedef DYNAMIC_ARRAY_BASE(void) DynamicArray; DynamicArray dyn_array; \ } -#ifdef USE_GNU_EXTENSIONS - #define DYNARRAY_ASSERT_VALID(darr) do { \ - static_assert( \ - __builtin_types_compatible_p(DynamicArray, __typeof__((darr)->dyn_array)), \ - "x->dyn_array must be of DynamicArray type"); \ - static_assert( \ - __builtin_offsetof(__typeof__(*(darr)), dyn_array) == 0, \ - "x->dyn_array must be the first member in struct"); \ - } while(0) -#else - #define DYNARRAY_ASSERT_VALID(darr) ((void)0) -#endif +#define DYNARRAY_ASSERT_VALID(darr) do { \ + static_assert( \ + __builtin_types_compatible_p(DynamicArray, __typeof__((darr)->dyn_array)), \ + "x->dyn_array must be of DynamicArray type"); \ + static_assert( \ + __builtin_offsetof(__typeof__(*(darr)), dyn_array) == 0, \ + "x->dyn_array must be the first member in struct"); \ +} while(0) -#ifdef USE_GNU_EXTENSIONS - #define DYNARRAY_CAST_TO_BASE(darr) (__extension__ ({ \ - DYNARRAY_ASSERT_VALID(darr); \ - &NOT_NULL(darr)->dyn_array; \ - })) -#else - #define DYNARRAY_CAST_TO_BASE(darr) (&(darr)->dyn_array) -#endif +#define DYNARRAY_CAST_TO_BASE(darr) ({ \ + DYNARRAY_ASSERT_VALID(darr); \ + &NOT_NULL(darr)->dyn_array; \ +}) -#ifdef USE_GNU_EXTENSIONS - #define DYNARRAY_CHECK_ELEMENT_TYPE(darr, elem) do { \ - static_assert( \ - __builtin_types_compatible_p(__typeof__(elem), __typeof__(*(darr)->data)), \ - "Dynamic array element type mismatch" \ - ); \ - } while(0) -#else - #define DYNARRAY_CHECK_ELEMENT_TYPE(darr, elem) do { \ - static_assert( \ - sizeof(elem) == sizeof(*(darr)->data), \ - "Dynamic array element size mismatch" \ - ); \ - } while(0) -#endif +#define DYNARRAY_CHECK_ELEMENT_TYPE(darr, elem) do { \ + static_assert( \ + __builtin_types_compatible_p(__typeof__(elem), __typeof__(*(darr)->data)), \ + "Dynamic array element type mismatch" \ + ); \ +} while(0) #define DYNARRAY_ELEM_SIZE(darr) sizeof(*(darr)->data) @@ -97,15 +80,10 @@ dynarray_size_t _dynarray_prepare_append_with_min_capacity(dynarray_size_t sizeo #define _dynarray_append_with_min_capacity(darr, min_capacity) \ dynarray_get_ptr(darr, _dynarray_dispatch_func(prepare_append_with_min_capacity, darr, min_capacity)) -#ifdef USE_GNU_EXTENSIONS - #define dynarray_append_with_min_capacity(darr, min_capacity) (__extension__ ({ \ - assume((darr) != NULL); \ - _dynarray_append_with_min_capacity((darr), min_capacity); \ - })) -#else - #define dynarray_append_with_min_capacity(darr, min_capacity) \ - _dynarray_append_with_min_capacity((darr), min_capacity) -#endif +#define dynarray_append_with_min_capacity(darr, min_capacity) ({ \ + __auto_type _darr2 = NOT_NULL(darr); \ + dynarray_get_ptr(_darr2, _dynarray_dispatch_func(prepare_append_with_min_capacity, _darr2, min_capacity)); \ +}) #define dynarray_append(darr) \ dynarray_append_with_min_capacity(darr, 2) @@ -121,23 +99,22 @@ void _dynarray_compact(dynarray_size_t sizeof_element, DynamicArray *darr) attr_ } \ } while(0) -#ifdef USE_GNU_EXTENSIONS - #define dynarray_get_ptr(darr, idx) (__extension__ ({ \ - DYNARRAY_ASSERT_VALID(darr); \ - __auto_type _darr = NOT_NULL(darr); \ - dynarray_size_t _darr_idx = (idx); \ - assume(_darr_idx >= 0); \ - assume(_darr_idx < _darr->num_elements); \ - NOT_NULL((darr)->data + _darr_idx); \ - })) -#else - #define dynarray_get_ptr(darr, idx) ((darr)->data + (idx)) -#endif +#define dynarray_get_ptr(darr, idx) ({ \ + DYNARRAY_ASSERT_VALID(darr); \ + __auto_type _darr = NOT_NULL(darr); \ + dynarray_size_t _darr_idx = (idx); \ + assume(_darr_idx >= 0); \ + assume(_darr_idx < _darr->num_elements); \ + NOT_NULL((darr)->data + _darr_idx); \ +}) -#define dynarray_get(darr, idx) (*dynarray_get_ptr(darr, idx)) +#define dynarray_get(darr, idx) (*dynarray_get_ptr(darr, idx)) #define dynarray_set(darr, idx, ...) ((*dynarray_get_ptr(darr, idx)) = (__VA_ARGS__)) -void _dynarray_set_elements(dynarray_size_t sizeof_element, DynamicArray *darr, dynarray_size_t num_elements, void *elements) attr_nonnull_all; +void _dynarray_set_elements( + dynarray_size_t sizeof_element, DynamicArray *darr, + dynarray_size_t num_elements, void *elements +) attr_nonnull_all; #define dynarray_set_elements(darr, num_elements, elements) do { \ DYNARRAY_ASSERT_VALID(darr); \ DYNARRAY_CHECK_ELEMENT_TYPE(darr, *(elements)); \ @@ -145,24 +122,23 @@ void _dynarray_set_elements(dynarray_size_t sizeof_element, DynamicArray *darr, } while(0) typedef bool (*dynarray_filter_predicate_t)(const void *pelem, void *userdata); -void _dynarray_filter(dynarray_size_t sizeof_element, DynamicArray *darr, dynarray_filter_predicate_t predicate, void *userdata) attr_nonnull(2, 3); +void _dynarray_filter( + dynarray_size_t sizeof_element, DynamicArray *darr, + dynarray_filter_predicate_t predicate, void *userdata +) attr_nonnull(2, 3); #define dynarray_filter(darr, predicate, userdata) \ _dynarray_dispatch_func(filter, darr, predicate, userdata) -#ifdef USE_GNU_EXTENSIONS - #define dynarray_indexof(darr, pelem) (__extension__ ({ \ - DYNARRAY_ASSERT_VALID(darr); \ - __auto_type _darr = NOT_NULL(darr); \ - __auto_type _darr_pelem = NOT_NULL(pelem); \ - DYNARRAY_CHECK_ELEMENT_TYPE(_darr, *(_darr_pelem)); \ - intptr_t _darr_idx = (intptr_t)(_darr_pelem - _darr->data); \ - assume(_darr_idx >= 0); \ - assume(_darr_idx < _darr->num_elements); \ - (dynarray_size_t)_darr_idx; \ - })) -#else - #define dynarray_indexof(darr, pelem) ((intptr_t)((pelem) - (darr)->data)) -#endif +#define dynarray_indexof(darr, pelem) ({ \ + DYNARRAY_ASSERT_VALID(darr); \ + __auto_type _darr = NOT_NULL(darr); \ + __auto_type _darr_pelem = NOT_NULL(pelem); \ + DYNARRAY_CHECK_ELEMENT_TYPE(_darr, *(_darr_pelem)); \ + intptr_t _darr_idx = (intptr_t)(_darr_pelem - _darr->data); \ + assume(_darr_idx >= 0); \ + assume(_darr_idx < _darr->num_elements); \ + (dynarray_size_t)_darr_idx; \ +}) #define _dynarray_foreach_iter MACROHAX_ADDLINENUM(_dynarray_foreach_iter) #define _dynarray_foreach_ignored MACROHAX_ADDLINENUM(_dynarray_foreach_ignored) @@ -179,9 +155,18 @@ void _dynarray_filter(dynarray_size_t sizeof_element, DynamicArray *darr, dynarr } while(0) #define dynarray_foreach_elem(_darr, _pelem_var, ...) \ - dynarray_foreach(_darr, attr_unused dynarray_size_t _dynarray_foreach_ignored, _pelem_var, __VA_ARGS__) + dynarray_foreach( \ + _darr, \ + attr_unused dynarray_size_t _dynarray_foreach_ignored, \ + _pelem_var, \ + __VA_ARGS__ \ + ) #define dynarray_foreach_idx(_darr, _cntr_var, ...) \ - dynarray_foreach(_darr, _cntr_var, attr_unused void *_dynarray_foreach_ignored, __VA_ARGS__) + dynarray_foreach(_darr, \ + _cntr_var, \ + attr_unused void *_dynarray_foreach_ignored, \ + __VA_ARGS__ \ + ) #endif // IGUARD_dynarray_h diff --git a/src/entity.h b/src/entity.h index a8601242..66c7f76a 100644 --- a/src/entity.h +++ b/src/entity.h @@ -125,15 +125,11 @@ INLINE const char *ent_type_name(EntityType type) { } } -#ifdef USE_GNU_EXTENSIONS - #define ENT_CAST(ent, typename) (__extension__ ({ \ - __auto_type _ent = ent; \ - assert(_ent->type == ENT_TYPE_ID(typename)); \ - UNION_CAST(EntityInterface*, typename*, _ent); \ - })) -#else - #define ENT_CAST(ent, typename) UNION_CAST(EntityInterface*, typename*, (ent)); -#endif +#define ENT_CAST(ent, typename) ({ \ + __auto_type _ent = ent; \ + assert(_ent->type == ENT_TYPE_ID(typename)); \ + UNION_CAST(EntityInterface*, typename*, _ent); \ +}) void ent_init(void); void ent_shutdown(void); diff --git a/src/eventloop/executor_emscripten.c b/src/eventloop/executor_emscripten.c index 5d941872..7bcf0a08 100644 --- a/src/eventloop/executor_emscripten.c +++ b/src/eventloop/executor_emscripten.c @@ -79,7 +79,7 @@ void eventloop_run(void) { em_handle_resize_event, NULL, EPRIO_SYSTEM, MAKE_TAISEI_EVENT(TE_VIDEO_MODE_CHANGED) }); - (__extension__ EM_ASM({ + EM_ASM({ Module['onFirstFrame'](); - })); + }); } diff --git a/src/list.h b/src/list.h index 31b7c949..d28896c7 100644 --- a/src/list.h +++ b/src/list.h @@ -101,42 +101,36 @@ ListContainer* list_wrap_container(void *data) attr_returns_allocated; // type-generic macros -#ifdef USE_GNU_EXTENSIONS - // thorough safeguard +#define LIST_CAST(expr) ({ \ + static_assert(__builtin_types_compatible_p( \ + ListInterface, __typeof__((*(expr)).list_interface)), \ + "struct must implement ListInterface (use the LIST_INTERFACE macro)"); \ + static_assert(__builtin_offsetof(__typeof__(*(expr)), list_interface) == 0, \ + "list_interface must be the first member in struct"); \ + CASTPTR_ASSUME_ALIGNED((expr), List); \ +}) - #define LIST_CAST(expr) (__extension__ ({ \ - static_assert(__builtin_types_compatible_p(ListInterface, __typeof__((*(expr)).list_interface)), \ - "struct must implement ListInterface (use the LIST_INTERFACE macro)"); \ - static_assert(__builtin_offsetof(__typeof__(*(expr)), list_interface) == 0, \ - "list_interface must be the first member in struct"); \ - CASTPTR_ASSUME_ALIGNED((expr), List); \ - })) +#define LIST_CAST_2(expr) ({ \ + static_assert(__builtin_types_compatible_p(\ + ListInterface, __typeof__((**(expr)).list_interface)), \ + "struct must implement ListInterface (use the LIST_INTERFACE macro)"); \ + static_assert(__builtin_offsetof(__typeof__(**(expr)), list_interface) == 0, \ + "list_interface must be the first member in struct"); \ + (void)ASSUME_ALIGNED(*(expr), alignof(List)); \ + (List**)(expr); \ +}) - #define LIST_CAST_2(expr) (__extension__ ({ \ - static_assert(__builtin_types_compatible_p(ListInterface, __typeof__((**(expr)).list_interface)), \ - "struct must implement ListInterface (use the LIST_INTERFACE macro)"); \ - static_assert(__builtin_offsetof(__typeof__(**(expr)), list_interface) == 0, \ - "list_interface must be the first member in struct"); \ - (void)ASSUME_ALIGNED(*(expr), alignof(List)); \ - (List**)(expr); \ - })) +#define LIST_ANCHOR_CAST(expr) ({ \ + static_assert(__builtin_types_compatible_p(\ + ListAnchorInterface, __typeof__((*(expr)).list_anchor_interface)), \ + "struct must implement ListAnchorInterface (use the LIST_ANCHOR_INTERFACE macro)"); \ + static_assert(__builtin_offsetof(__typeof__(*(expr)), list_anchor_interface) == 0, \ + "list_anchor_interface must be the first member in struct"); \ + CASTPTR_ASSUME_ALIGNED((expr), ListAnchor); \ +}) - #define LIST_ANCHOR_CAST(expr) (__extension__ ({ \ - static_assert(__builtin_types_compatible_p(ListAnchorInterface, __typeof__((*(expr)).list_anchor_interface)), \ - "struct must implement ListAnchorInterface (use the LIST_ANCHOR_INTERFACE macro)"); \ - static_assert(__builtin_offsetof(__typeof__(*(expr)), list_anchor_interface) == 0, \ - "list_anchor_interface must be the first member in struct"); \ - CASTPTR_ASSUME_ALIGNED((expr), ListAnchor); \ - })) - - #define LIST_CAST_RETURN(e_typekey, e_return) CASTPTR_ASSUME_ALIGNED((e_return), __typeof__(*(e_typekey))) -#else - // basic safeguard - #define LIST_CAST(expr) ((void)sizeof((*(expr)).list_interface), (List*)(expr)) - #define LIST_CAST_2(expr) ((void)sizeof((**(expr)).list_interface), (List**)(expr)) - #define LIST_ANCHOR_CAST(expr) ((void)sizeof((*(expr)).list_anchor_interface), (ListAnchor*)(expr)) - #define LIST_CAST_RETURN(e_typekey, e_return) (void*)(e_return) -#endif +#define LIST_CAST_RETURN(e_typekey, e_return) \ + CASTPTR_ASSUME_ALIGNED((e_return), __typeof__(*(e_typekey))) #define list_insert(dest,elem) \ (LIST_CAST_RETURN(elem, list_insert(LIST_CAST_2(dest), LIST_CAST(elem)))) diff --git a/src/util/assert.c b/src/util/assert.c index a97b4894..314aa9c2 100644 --- a/src/util/assert.c +++ b/src/util/assert.c @@ -26,8 +26,8 @@ void _ts_assert_fail(const char *cond, const char *func, const char *file, int l #ifdef __EMSCRIPTEN__ #include void _emscripten_trap(void) { - (__extension__ EM_ASM({ + EM_ASM({ throw new Error("You just activated my trap card!"); - })); + }); } #endif diff --git a/src/util/compat.h b/src/util/compat.h index 95706a54..185fad4a 100644 --- a/src/util/compat.h +++ b/src/util/compat.h @@ -76,12 +76,9 @@ #define PRAGMA(p) _Pragma(#p) -#ifdef RNG_API_CHECK - #undef TAISEI_BUILDCONF_USE_GNU_EXTENSIONS - #undef USE_GNU_EXTENSIONS -#endif - #ifndef __GNUC__ // clang defines this too + #pragma Unsupported compiler, expect nothing to work + #define __attribute__(...) #define __extension__ #define UNREACHABLE @@ -91,9 +88,6 @@ #define LIKELY(x) (bool)(x) #define UNLIKELY(x) (bool)(x) #else - #ifdef TAISEI_BUILDCONF_USE_GNU_EXTENSIONS - #define USE_GNU_EXTENSIONS - #endif #define UNREACHABLE __builtin_unreachable() #define DIAGNOSTIC(x) PRAGMA(GCC diagnostic x) @@ -292,32 +286,23 @@ typedef _Complex double cmplx; #define INLINE static inline attr_must_inline __attribute__((gnu_inline)) -#ifdef USE_GNU_EXTENSIONS - #define ASSUME_ALIGNED(expr, alignment) (__extension__ ({ \ - static_assert(__builtin_constant_p(alignment), ""); \ - __auto_type _assume_aligned_ptr = (expr); \ - assert(((uintptr_t)_assume_aligned_ptr & ((alignment) - 1)) == 0); \ - __builtin_assume_aligned(_assume_aligned_ptr, (alignment)); \ - })) -#else - #define ASSUME_ALIGNED(expr, alignment) (expr) -#endif +#define ASSUME_ALIGNED(expr, alignment) ({ \ + static_assert(__builtin_constant_p(alignment), ""); \ + __auto_type _assume_aligned_ptr = (expr); \ + assert(((uintptr_t)_assume_aligned_ptr & ((alignment) - 1)) == 0); \ + __builtin_assume_aligned(_assume_aligned_ptr, (alignment)); \ +}) #define UNION_CAST(_from_type, _to_type, _expr) \ ((union { _from_type f; _to_type t; }) { .f = (_expr) }).t -// #define CASTPTR_ASSUME_ALIGNED(expr, type) UNION_CAST(void*, type*, ASSUME_ALIGNED(expr, alignof(type))) #define CASTPTR_ASSUME_ALIGNED(expr, type) ((type*)ASSUME_ALIGNED((expr), alignof(type))) -#ifdef USE_GNU_EXTENSIONS - #define NOT_NULL(expr) (__extension__ ({ \ - __auto_type _assume_not_null_ptr = (expr); \ - assume(_assume_not_null_ptr != NULL); \ - _assume_not_null_ptr; \ - })) -#else - #define NOT_NULL(expr) (expr) -#endif +#define NOT_NULL(expr) ({ \ + __auto_type _assume_not_null_ptr = (expr); \ + assume(_assume_not_null_ptr != NULL); \ + _assume_not_null_ptr; \ +}) #ifdef __SWITCH__ #include "../arch_switch.h" diff --git a/src/util/miscmath.c b/src/util/miscmath.c index 4d148f65..79bd0375 100644 --- a/src/util/miscmath.c +++ b/src/util/miscmath.c @@ -506,7 +506,7 @@ uint64_t _umuldiv64_slow(uint64_t x, uint64_t multiplier, uint64_t divisor) { INLINE uint64_t _umuldiv64(uint64_t x, uint64_t multiplier, uint64_t divisor) { #if defined(TAISEI_BUILDCONF_HAVE_INT128) - __extension__ typedef unsigned __int128 uint128_t; + typedef unsigned __int128 uint128_t; return ((uint128_t)x * (uint128_t)multiplier) / divisor; #elif defined(TAISEI_BUILDCONF_HAVE_LONG_DOUBLE) #define UMULDIV64_SANITY_CHECK diff --git a/src/vfs/sync_emscripten.c b/src/vfs/sync_emscripten.c index 379d0c22..4b5dbaec 100644 --- a/src/vfs/sync_emscripten.c +++ b/src/vfs/sync_emscripten.c @@ -36,7 +36,7 @@ void vfs_sync_callback(bool is_load, char *error, CallChain *next) { void vfs_sync(VFSSyncMode mode, CallChain next) { CallChain *cc = memdup(&next, sizeof(next)); - __extension__ (EM_ASM({ + EM_ASM({ SyncFS($0, $1); - }, (mode == VFS_SYNC_LOAD), cc)); + }, (mode == VFS_SYNC_LOAD), cc); }