From ea473fb2dee7dfe61f8fbdfac9d9da87d84e564e Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 30 Sep 2012 14:38:31 -0400 Subject: [PATCH] Add infrastructure for compile-time assertions about variable types. Currently, the macros only work with fairly recent gcc versions, but there is room to expand them to other compilers that have comparable features. Heavily revised and autoconfiscated version of a patch by Andres Freund. --- config/c-compiler.m4 | 40 ++++++++++++ configure | 119 ++++++++++++++++++++++++++++++++++ configure.in | 2 + src/include/c.h | 41 ++++++++++++ src/include/pg_config.h.in | 6 ++ src/include/pg_config.h.win32 | 6 ++ 6 files changed, 214 insertions(+) diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index 9398ca6c47..069b468daa 100644 --- a/config/c-compiler.m4 +++ b/config/c-compiler.m4 @@ -121,6 +121,46 @@ fi])# PGAC_C_FUNCNAME_SUPPORT +# PGAC_C_STATIC_ASSERT +# ----------------------- +# Check if the C compiler understands _Static_assert(), +# and define HAVE__STATIC_ASSERT if so. +# +# We actually check the syntax ({ _Static_assert(...) }), because we need +# gcc-style compound expressions to be able to wrap the thing into macros. +AC_DEFUN([PGAC_C_STATIC_ASSERT], +[AC_CACHE_CHECK(for _Static_assert, pgac_cv__static_assert, +[AC_TRY_LINK([], +[({ _Static_assert(1, "foo"); })], +[pgac_cv__static_assert=yes], +[pgac_cv__static_assert=no])]) +if test x"$pgac_cv__static_assert" = xyes ; then +AC_DEFINE(HAVE__STATIC_ASSERT, 1, + [Define to 1 if your compiler understands _Static_assert.]) +fi])# PGAC_C_STATIC_ASSERT + + + +# PGAC_C_TYPES_COMPATIBLE +# ----------------------- +# Check if the C compiler understands __builtin_types_compatible_p, +# and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so. +# +# We check usage with __typeof__, though it's unlikely any compiler would +# have the former and not the latter. +AC_DEFUN([PGAC_C_TYPES_COMPATIBLE], +[AC_CACHE_CHECK(for __builtin_types_compatible_p, pgac_cv__types_compatible, +[AC_TRY_COMPILE([], +[ int x; static int y[__builtin_types_compatible_p(__typeof__(x), int)]; ], +[pgac_cv__types_compatible=yes], +[pgac_cv__types_compatible=no])]) +if test x"$pgac_cv__types_compatible" = xyes ; then +AC_DEFINE(HAVE__BUILTIN_TYPES_COMPATIBLE_P, 1, + [Define to 1 if your compiler understands __builtin_types_compatible_p.]) +fi])# PGAC_C_TYPES_COMPATIBLE + + + # PGAC_PROG_CC_CFLAGS_OPT # ----------------------- # Given a string, check if the compiler supports the string as a diff --git a/configure b/configure index 56da7cdc73..c6dcc8bfaa 100755 --- a/configure +++ b/configure @@ -15524,6 +15524,125 @@ cat >>confdefs.h <<\_ACEOF _ACEOF fi +fi +{ $as_echo "$as_me:$LINENO: checking for _Static_assert" >&5 +$as_echo_n "checking for _Static_assert... " >&6; } +if test "${pgac_cv__static_assert+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +({ _Static_assert(1, "foo"); }) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + pgac_cv__static_assert=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv__static_assert=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $pgac_cv__static_assert" >&5 +$as_echo "$pgac_cv__static_assert" >&6; } +if test x"$pgac_cv__static_assert" = xyes ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE__STATIC_ASSERT 1 +_ACEOF + +fi +{ $as_echo "$as_me:$LINENO: checking for __builtin_types_compatible_p" >&5 +$as_echo_n "checking for __builtin_types_compatible_p... " >&6; } +if test "${pgac_cv__types_compatible+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + int x; static int y[__builtin_types_compatible_p(__typeof__(x), int)]; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + pgac_cv__types_compatible=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv__types_compatible=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $pgac_cv__types_compatible" >&5 +$as_echo "$pgac_cv__types_compatible" >&6; } +if test x"$pgac_cv__types_compatible" = xyes ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE__BUILTIN_TYPES_COMPATIBLE_P 1 +_ACEOF + fi { $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } diff --git a/configure.in b/configure.in index 8d02abb4cc..fef8e7f013 100644 --- a/configure.in +++ b/configure.in @@ -1104,6 +1104,8 @@ AC_C_FLEXIBLE_ARRAY_MEMBER PGAC_C_SIGNED AC_C_VOLATILE PGAC_C_FUNCNAME_SUPPORT +PGAC_C_STATIC_ASSERT +PGAC_C_TYPES_COMPATIBLE PGAC_STRUCT_TIMEZONE PGAC_UNION_SEMUN PGAC_STRUCT_SOCKADDR_UN diff --git a/src/include/c.h b/src/include/c.h index 50e1ecfde4..06f689d359 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -689,6 +689,47 @@ typedef NameData *Name; } while (0) +/* + * Macros to support compile-time assertion checks, if the compiler has them. + * + * If the "condition" (a compile-time-constant expression) evaluates to false, + * throw a compile error using the "errmessage" (a string literal). + * + * gcc 4.6 and up supports _Static_assert(), but it has bizarre syntactic + * placement restrictions. These macros make it safe to use as a statement + * or in an expression, respectively. + */ +#ifdef HAVE__STATIC_ASSERT +#define StaticAssertStmt(condition, errmessage) \ + do { _Static_assert(condition, errmessage); } while(0) +#define StaticAssertExpr(condition, errmessage) \ + ({ StaticAssertStmt(condition, errmessage); true; }) +#else /* !HAVE__STATIC_ASSERT */ +#define StaticAssertStmt(condition, errmessage) +#define StaticAssertExpr(condition, errmessage) ((void) true) +#endif /* HAVE__STATIC_ASSERT */ + + +/* + * Compile-time checks that a variable (or expression) has the specified type. + * + * AssertVariableIsOfType() can be used as a statement. + * AssertVariableIsOfTypeMacro() is intended for use in macros, eg + * #define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x)) + */ +#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P +#define AssertVariableIsOfType(varname, typename) \ + StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \ + CppAsString(varname) " does not have type " CppAsString(typename)) +#define AssertVariableIsOfTypeMacro(varname, typename) \ + StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \ + CppAsString(varname) " does not have type " CppAsString(typename)) +#else /* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */ +#define AssertVariableIsOfType(varname, typename) +#define AssertVariableIsOfTypeMacro(varname, typename) ((void) true) +#endif /* HAVE__BUILTIN_TYPES_COMPATIBLE_P */ + + /* ---------------------------------------------------------------- * Section 7: random stuff * ---------------------------------------------------------------- diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 1ffb032835..58cd5907dd 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -635,6 +635,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_WINLDAP_H +/* Define to 1 if your compiler understands __builtin_types_compatible_p. */ +#undef HAVE__BUILTIN_TYPES_COMPATIBLE_P + +/* Define to 1 if your compiler understands _Static_assert. */ +#undef HAVE__STATIC_ASSERT + /* Define to the appropriate snprintf format for 64-bit ints. */ #undef INT64_FORMAT diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index b7b7c641d3..4d9cc55c69 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -526,6 +526,12 @@ /* Define to 1 if you have the header file. */ /* #undef HAVE_WINLDAP_H */ +/* Define to 1 if your compiler understands __builtin_types_compatible_p. */ +/* #undef HAVE__BUILTIN_TYPES_COMPATIBLE_P */ + +/* Define to 1 if your compiler understands _Static_assert. */ +/* #undef HAVE__STATIC_ASSERT */ + /* Define to the appropriate snprintf format for 64-bit ints, if any. */ #define INT64_FORMAT "%lld" -- GitLab