On Tue, Dec 24, 2019 at 02:47:29PM +0900, Michael Paquier wrote: > I am still seeing that a couple of points need an extra lookup: > - Addition of a Decl() in at least one header of the core code.
I agree with the addition of Decl() definition in a header, and could not think about something better than one for bufpage.h for the all-zero check case, so I went with that. Attached is a 0001 which adds the definition for StaticAssertDecl() for C and C++ for all code paths. If there are no objections, I would like to commit this version. There is no fancy refactoring in it, and small progress is better than no progress. I have also reworked the comments in the patch, and did some testing on Windows. > - Perhaps unifying the fallback implementation between C and C++, with > a closer lookup in particular at StaticAssertStmt() and StaticAssertExpr(). Seeing nothing happening on this side. I took a shot at all that, and I have hacked my way through it with 0002 which is an attempt to unify the fallback implementation for C and C++. This is not fully baked yet, and it is perhaps a matter of taste if this makes the code more readable or not. I think it does, because it reduces the parts dedicated to assertion definitions from four to three. Anyway, let's discuss about that. -- Michael
From 3872af56ad51ba2f6d927ffd6e7f79a0bd08551a Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Fri, 31 Jan 2020 10:34:30 +0900 Subject: [PATCH 1/2] Add declaration-level assertions --- src/include/c.h | 24 ++++++++++++------ src/include/storage/bufpage.h | 10 ++++++++ src/backend/storage/page/bufpage.c | 9 +------ src/backend/utils/adt/lockfuncs.c | 6 +++++ src/backend/utils/misc/guc.c | 39 ++++++++++++++++++++++++++++++ src/common/relpath.c | 3 +++ src/bin/pg_dump/pg_dump_sort.c | 3 +++ 7 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/include/c.h b/src/include/c.h index 6898229b43..b2966f9edf 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -826,14 +826,16 @@ extern void ExceptionalCondition(const char *conditionName, #endif /* - * Macros to support compile-time assertion checks. + * Macros to support compile-time and declaration assertion checks. * - * If the "condition" (a compile-time-constant expression) evaluates to false, - * throw a compile error using the "errmessage" (a string literal). + * If the "condition" (a compile-time-constant or declaration expression) + * evaluates to false, throw a compile error using the "errmessage" (a + * string literal). * * gcc 4.6 and up supports _Static_assert(), but there are bizarre syntactic - * placement restrictions. These macros make it safe to use as a statement - * or in an expression, respectively. + * placement restrictions. Compile-time macros make it safe to use as a + * statement or in an expression, respectively. Declaration macros are + * useful at file scope. * * Otherwise we fall back on a kluge that assumes the compiler will complain * about a negative width for a struct bit-field. This will not include a @@ -845,11 +847,15 @@ extern void ExceptionalCondition(const char *conditionName, do { _Static_assert(condition, errmessage); } while(0) #define StaticAssertExpr(condition, errmessage) \ ((void) ({ StaticAssertStmt(condition, errmessage); true; })) +#define StaticAssertDecl(condition, errmessage) \ + _Static_assert(condition, errmessage) #else /* !HAVE__STATIC_ASSERT */ #define StaticAssertStmt(condition, errmessage) \ ((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; })) #define StaticAssertExpr(condition, errmessage) \ StaticAssertStmt(condition, errmessage) +#define StaticAssertDecl(condition, errmessage) \ + extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1]) #endif /* HAVE__STATIC_ASSERT */ #else /* C++ */ #if defined(__cpp_static_assert) && __cpp_static_assert >= 200410 @@ -857,12 +863,16 @@ extern void ExceptionalCondition(const char *conditionName, static_assert(condition, errmessage) #define StaticAssertExpr(condition, errmessage) \ ({ static_assert(condition, errmessage); }) -#else +#define StaticAssertDecl(condition, errmessage) \ + static_assert(condition, errmessage) +#else /* !__cpp_static_assert */ #define StaticAssertStmt(condition, errmessage) \ do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0) #define StaticAssertExpr(condition, errmessage) \ ((void) ({ StaticAssertStmt(condition, errmessage); })) -#endif +#define StaticAssertDecl(condition, errmessage) \ + extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1]) +#endif /* __cpp_static_assert */ #endif /* C++ */ diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 870ecb51b7..3f88683a05 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -418,6 +418,16 @@ do { \ ((overwrite) ? PAI_OVERWRITE : 0) | \ ((is_heap) ? PAI_IS_HEAP : 0)) +/* + * Check that BLCKSZ is a multiple of sizeof(size_t). In PageIsVerified(), + * it is much faster to check if a page is full of zeroes using the native + * word size. Note that this assertion is kept within a header to make + * sure that StaticAssertDecl() works across various combinations of + * platforms and compilers. + */ +StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)), + "BLCKSZ has to be a multiple of sizeof(size_t)"); + extern void PageInit(Page page, Size pageSize, Size specialSize); extern bool PageIsVerified(Page page, BlockNumber blkno); extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size, diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index f47176753d..4ea6ea7a3d 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -119,14 +119,7 @@ PageIsVerified(Page page, BlockNumber blkno) return true; } - /* - * Check all-zeroes case. Luckily BLCKSZ is guaranteed to always be a - * multiple of size_t - and it's much faster to compare memory using the - * native word size. - */ - StaticAssertStmt(BLCKSZ == (BLCKSZ / sizeof(size_t)) * sizeof(size_t), - "BLCKSZ has to be a multiple of sizeof(size_t)"); - + /* Check all-zeroes case */ all_zeroes = true; pagebytes = (size_t *) page; for (i = 0; i < (BLCKSZ / sizeof(size_t)); i++) diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index 9a7da12a5b..7e47ebeb6f 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -36,6 +36,9 @@ const char *const LockTagTypeNames[] = { "advisory" }; +StaticAssertDecl(lengthof(LockTagTypeNames) == (LOCKTAG_ADVISORY + 1), + "array length mismatch"); + /* This must match enum PredicateLockTargetType (predicate_internals.h) */ static const char *const PredicateLockTagTypeNames[] = { "relation", @@ -43,6 +46,9 @@ static const char *const PredicateLockTagTypeNames[] = { "tuple" }; +StaticAssertDecl(lengthof(PredicateLockTagTypeNames) == (PREDLOCKTAG_TUPLE + 1), + "array length mismatch"); + /* Working status for pg_lock_status */ typedef struct { diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a16fe8cd5b..9630866a5f 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -241,6 +241,9 @@ static const struct config_enum_entry bytea_output_options[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(bytea_output_options) == (BYTEA_OUTPUT_HEX + 2), + "array length mismatch"); + /* * We have different sets for client and server message level options because * they sort slightly different (see "log" level), and because "fatal"/"panic" @@ -286,6 +289,9 @@ static const struct config_enum_entry intervalstyle_options[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(intervalstyle_options) == (INTSTYLE_ISO_8601 + 2), + "array length mismatch"); + static const struct config_enum_entry log_error_verbosity_options[] = { {"terse", PGERROR_TERSE, false}, {"default", PGERROR_DEFAULT, false}, @@ -293,6 +299,9 @@ static const struct config_enum_entry log_error_verbosity_options[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(log_error_verbosity_options) == (PGERROR_VERBOSE + 2), + "array length mismatch"); + static const struct config_enum_entry log_statement_options[] = { {"none", LOGSTMT_NONE, false}, {"ddl", LOGSTMT_DDL, false}, @@ -301,6 +310,9 @@ static const struct config_enum_entry log_statement_options[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(log_statement_options) == (LOGSTMT_ALL + 2), + "array length mismatch"); + static const struct config_enum_entry isolation_level_options[] = { {"serializable", XACT_SERIALIZABLE, false}, {"repeatable read", XACT_REPEATABLE_READ, false}, @@ -316,6 +328,9 @@ static const struct config_enum_entry session_replication_role_options[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(session_replication_role_options) == (SESSION_REPLICATION_ROLE_LOCAL + 2), + "array length mismatch"); + static const struct config_enum_entry syslog_facility_options[] = { #ifdef HAVE_SYSLOG {"local0", LOG_LOCAL0, false}, @@ -339,18 +354,27 @@ static const struct config_enum_entry track_function_options[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(track_function_options) == (TRACK_FUNC_ALL + 2), + "array length mismatch"); + static const struct config_enum_entry xmlbinary_options[] = { {"base64", XMLBINARY_BASE64, false}, {"hex", XMLBINARY_HEX, false}, {NULL, 0, false} }; +StaticAssertDecl(lengthof(xmlbinary_options) == (XMLBINARY_HEX + 2), + "array length mismatch"); + static const struct config_enum_entry xmloption_options[] = { {"content", XMLOPTION_CONTENT, false}, {"document", XMLOPTION_DOCUMENT, false}, {NULL, 0, false} }; +StaticAssertDecl(lengthof(xmloption_options) == (XMLOPTION_CONTENT + 2), + "array length mismatch"); + /* * Although only "on", "off", and "safe_encoding" are documented, we * accept all the likely variants of "on" and "off". @@ -465,6 +489,9 @@ const struct config_enum_entry ssl_protocol_versions_info[] = { {NULL, 0, false} }; +StaticAssertDecl(lengthof(ssl_protocol_versions_info) == (PG_TLS1_3_VERSION + 2), + "array length mismatch"); + static struct config_enum_entry shared_memory_options[] = { #ifndef WIN32 {"sysv", SHMEM_TYPE_SYSV, false}, @@ -615,6 +642,9 @@ const char *const GucContext_Names[] = /* PGC_USERSET */ "user" }; +StaticAssertDecl(lengthof(GucContext_Names) == (PGC_USERSET + 1), + "array length mismatch"); + /* * Displayable names for source types (enum GucSource) * @@ -638,6 +668,9 @@ const char *const GucSource_Names[] = /* PGC_S_SESSION */ "session" }; +StaticAssertDecl(lengthof(GucSource_Names) == (PGC_S_SESSION + 1), + "array length mismatch"); + /* * Displayable names for the groupings defined in enum config_group */ @@ -749,6 +782,9 @@ const char *const config_group_names[] = NULL }; +StaticAssertDecl(lengthof(config_group_names) == (DEVELOPER_OPTIONS + 2), + "array length mismatch"); + /* * Displayable names for GUC variable types (enum config_type) * @@ -763,6 +799,9 @@ const char *const config_type_names[] = /* PGC_ENUM */ "enum" }; +StaticAssertDecl(lengthof(config_type_names) == (PGC_ENUM + 1), + "array length mismatch"); + /* * Unit conversion tables. * diff --git a/src/common/relpath.c b/src/common/relpath.c index 91efcedf70..ad733d1363 100644 --- a/src/common/relpath.c +++ b/src/common/relpath.c @@ -37,6 +37,9 @@ const char *const forkNames[] = { "init" /* INIT_FORKNUM */ }; +StaticAssertDecl(lengthof(forkNames) == (MAX_FORKNUM + 1), + "array length mismatch"); + /* * forkname_to_number - look up fork number by name * diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 464798cf5c..1432dd7e0d 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -80,6 +80,9 @@ static const int dbObjectTypePriority[] = 38 /* DO_SUBSCRIPTION */ }; +StaticAssertDecl(lengthof(dbObjectTypePriority) == (DO_SUBSCRIPTION + 1), + "array length mismatch"); + static DumpId preDataBoundId; static DumpId postDataBoundId; -- 2.25.0
From d31bfc1b70e3dca83ce4d6d73776d519790359bd Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Fri, 31 Jan 2020 11:37:10 +0900 Subject: [PATCH 2/2] Refactor assertion definitions in c.h This unifies the C and C++ fallback implementations. --- src/include/c.h | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/include/c.h b/src/include/c.h index b2966f9edf..22d77e8d0c 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -841,39 +841,32 @@ extern void ExceptionalCondition(const char *conditionName, * about a negative width for a struct bit-field. This will not include a * helpful error message, but it beats not getting an error at all. */ -#ifndef __cplusplus -#ifdef HAVE__STATIC_ASSERT +#if !defined(__cplusplus) && defined(HAVE__STATIC_ASSERT) +/* Default C implementation */ #define StaticAssertStmt(condition, errmessage) \ do { _Static_assert(condition, errmessage); } while(0) #define StaticAssertExpr(condition, errmessage) \ ((void) ({ StaticAssertStmt(condition, errmessage); true; })) #define StaticAssertDecl(condition, errmessage) \ _Static_assert(condition, errmessage) -#else /* !HAVE__STATIC_ASSERT */ +#elif defined(__cplusplus) && \ + defined(__cpp_static_assert) && __cpp_static_assert >= 200410 +/* Default C++ implementation */ #define StaticAssertStmt(condition, errmessage) \ - ((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; })) + static_assert(condition, errmessage) #define StaticAssertExpr(condition, errmessage) \ + ({ StaticAssertStmt(condition, errmessage); }) +#define StaticAssertDecl(condition, errmessage) \ StaticAssertStmt(condition, errmessage) -#define StaticAssertDecl(condition, errmessage) \ - extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1]) -#endif /* HAVE__STATIC_ASSERT */ -#else /* C++ */ -#if defined(__cpp_static_assert) && __cpp_static_assert >= 200410 -#define StaticAssertStmt(condition, errmessage) \ - static_assert(condition, errmessage) -#define StaticAssertExpr(condition, errmessage) \ - ({ static_assert(condition, errmessage); }) -#define StaticAssertDecl(condition, errmessage) \ - static_assert(condition, errmessage) -#else /* !__cpp_static_assert */ +#else +/* Fallback implementation for C and C++ */ #define StaticAssertStmt(condition, errmessage) \ do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0) #define StaticAssertExpr(condition, errmessage) \ ((void) ({ StaticAssertStmt(condition, errmessage); })) #define StaticAssertDecl(condition, errmessage) \ extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1]) -#endif /* __cpp_static_assert */ -#endif /* C++ */ +#endif /* -- 2.25.0
signature.asc
Description: PGP signature