[PROPOSED 0/5] Finish off support for C23 standard attributes
The attached patches update tzcode to use the remaining C23 standard attributes that look useful for tzcode. I don't see any tzcode use for the C23 attributes 'deprecated' and 'nodiscard'. Paul Eggert (5): Use C23 [[noreturn]] if available Pacify gcc -std=c89 -pedantic with new attributes Use C23 [[reproducible]] if available. Use C23 [[unsequenced]] if available Use C23 [[fallthrough]] if available Makefile | 2 +- date.c | 2 +- localtime.c | 4 +-- private.h | 77 ++++++++++++++++++++++++++++++++++++++++++++--------- zdump.c | 10 +++---- zic.c | 32 +++++++++++----------- 6 files changed, 90 insertions(+), 37 deletions(-) -- 2.37.2
* private.h (ATTRIBUTE_NORETURN): Rename from _Noreturn, which C23 says is obsolescent, and use C23-style [[noreturn]] if available. All uses of _Noreturn changed. --- date.c | 2 +- private.h | 15 +++++++++++---- zdump.c | 2 +- zic.c | 8 ++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/date.c b/date.c index 13194629..11c5e5fe 100644 --- a/date.c +++ b/date.c @@ -42,7 +42,7 @@ static void display(const char *, time_t); static void dogmt(void); static void errensure(void); static void timeout(FILE *, const char *, const struct tm *); -static _Noreturn void usage(void); +static ATTRIBUTE_NORETURN void usage(void); int main(const int argc, char *argv[]) diff --git a/private.h b/private.h index 84facded..fa46094b 100644 --- a/private.h +++ b/private.h @@ -446,11 +446,18 @@ typedef unsigned long uintmax_t; # endif #endif -#if !defined _Noreturn && __STDC_VERSION__ < 201112 -# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__) -# define _Noreturn __attribute__((noreturn)) +#ifdef __has_c_attribute +# if __has_c_attribute(noreturn) +# define ATTRIBUTE_NORETURN [[noreturn]] +# endif +#endif +#ifndef ATTRIBUTE_NORETURN +# if 201112 <= __STDC_VERSION__ +# define ATTRIBUTE_NORETURN _Noreturn +# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__) +# define ATTRIBUTE_NORETURN __attribute__((noreturn)) # else -# define _Noreturn +# define ATTRIBUTE_NORETURN /* empty */ # endif #endif diff --git a/zdump.c b/zdump.c index de1e1116..99723ac0 100644 --- a/zdump.c +++ b/zdump.c @@ -125,7 +125,7 @@ is_alpha(char a) } } -static _Noreturn void +static ATTRIBUTE_NORETURN void size_overflow(void) { fprintf(stderr, _("%s: size overflow\n"), progname); diff --git a/zic.c b/zic.c index 7a7a5094..bd0cf4cd 100644 --- a/zic.c +++ b/zic.c @@ -459,14 +459,14 @@ static char roll[TZ_MAX_LEAPS]; ** Memory allocation. */ -static _Noreturn void +static ATTRIBUTE_NORETURN void memory_exhausted(const char *msg) { fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg); exit(EXIT_FAILURE); } -static _Noreturn void +static ATTRIBUTE_NORETURN void size_overflow(void) { memory_exhausted(_("size overflow")); @@ -666,7 +666,7 @@ close_file(FILE *stream, char const *dir, char const *name, } } -static _Noreturn void +static ATTRIBUTE_NORETURN void usage(FILE *stream, int status) { fprintf(stream, @@ -3725,7 +3725,7 @@ getfields(char *cp, char **array, int arrayelts) return nsubs; } -static _Noreturn void +static ATTRIBUTE_NORETURN void time_overflow(void) { error(_("time overflow")); -- 2.37.2
* private.h (__STDC_VERSION__): Default to 0. (ATTRIBUTE_MAYBE_UNUSED, ATTRIBUTE_NORETURN): Avoid diagnostic about use of [[...]] with 'gcc -std=c89 -pedantic'. --- private.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/private.h b/private.h index fa46094b..b9803427 100644 --- a/private.h +++ b/private.h @@ -17,6 +17,10 @@ ** Thank you! */ +#ifndef __STDC_VERSION__ +# define __STDC_VERSION__ 0 +#endif + /* Define true, false and bool if they don't work out of the box. */ #if __STDC_VERSION__ < 199901 # define true 1 @@ -433,7 +437,14 @@ typedef unsigned long uintmax_t; # define ATTRIBUTE_FORMAT(spec) /* empty */ #endif -#ifdef __has_c_attribute +#if (defined __has_c_attribute \ + && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__)) +# define HAVE_HAS_C_ATTRIBUTE true +#else +# define HAVE_HAS_C_ATTRIBUTE false +#endif + +#if HAVE_HAS_C_ATTRIBUTE # if __has_c_attribute(maybe_unused) # define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]] # endif @@ -446,7 +457,7 @@ typedef unsigned long uintmax_t; # endif #endif -#ifdef __has_c_attribute +#if HAVE_HAS_C_ATTRIBUTE # if __has_c_attribute(noreturn) # define ATTRIBUTE_NORETURN [[noreturn]] # endif -- 2.37.2
* private.h (ATTRIBUTE_REPRODUCIBLE): Rename from ATTRIBUTE_PURE, and use C23 style [[reproducible]] if available. All uses changed. --- localtime.c | 4 ++-- private.h | 19 +++++++++++++++---- zdump.c | 6 +++--- zic.c | 16 ++++++++-------- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/localtime.c b/localtime.c index 4dd9d259..1d22d351 100644 --- a/localtime.c +++ b/localtime.c @@ -838,7 +838,7 @@ is_digit(char c) ** Return a pointer to that character. */ -static ATTRIBUTE_PURE const char * +static ATTRIBUTE_REPRODUCIBLE const char * getzname(register const char *strp) { register char c; @@ -859,7 +859,7 @@ getzname(register const char *strp) ** We don't do any checking here; checking is done later in common-case code. */ -static ATTRIBUTE_PURE const char * +static ATTRIBUTE_REPRODUCIBLE const char * getqzname(register const char *strp, const int delim) { register int c; diff --git a/private.h b/private.h index b9803427..643068a7 100644 --- a/private.h +++ b/private.h @@ -428,12 +428,10 @@ typedef unsigned long uintmax_t; #if 3 <= __GNUC__ # define ATTRIBUTE_CONST __attribute__((const)) # define ATTRIBUTE_MALLOC __attribute__((malloc)) -# define ATTRIBUTE_PURE __attribute__((pure)) # define ATTRIBUTE_FORMAT(spec) __attribute__((format spec)) #else # define ATTRIBUTE_CONST /* empty */ # define ATTRIBUTE_MALLOC /* empty */ -# define ATTRIBUTE_PURE /* empty */ # define ATTRIBUTE_FORMAT(spec) /* empty */ #endif @@ -472,6 +470,19 @@ typedef unsigned long uintmax_t; # endif #endif +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(reproducible) +# define ATTRIBUTE_REPRODUCIBLE [[reproducible]] +# endif +#endif +#ifndef ATTRIBUTE_REPRODUCIBLE +# if 3 <= __GNUC__ +# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure)) +# else +# define ATTRIBUTE_REPRODUCIBLE /* empty */ +# endif +#endif + #if __STDC_VERSION__ < 199901 && !defined restrict # define restrict /* empty */ #endif @@ -705,10 +716,10 @@ timezone_t tzalloc(char const *); void tzfree(timezone_t); # ifdef STD_INSPIRED # if TZ_TIME_T || !defined posix2time_z -time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE; +time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_REPRODUCIBLE; # endif # if TZ_TIME_T || !defined time2posix_z -time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE; +time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_REPRODUCIBLE; # endif # endif #endif diff --git a/zdump.c b/zdump.c index 99723ac0..47141655 100644 --- a/zdump.c +++ b/zdump.c @@ -89,7 +89,7 @@ static bool warned; static bool errout; static char const *abbr(struct tm const *); -static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE; +static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_REPRODUCIBLE; static void dumptime(struct tm const *); static time_t hunt(timezone_t, time_t, time_t, bool); static void show(timezone_t, char *, time_t, bool); @@ -97,7 +97,7 @@ static void showextrema(timezone_t, char *, time_t, struct tm *, time_t); static void showtrans(char const *, struct tm const *, time_t, char const *, char const *); static const char *tformat(void); -static time_t yeartot(intmax_t) ATTRIBUTE_PURE; +static time_t yeartot(intmax_t) ATTRIBUTE_REPRODUCIBLE; /* Is C an ASCII digit? */ static bool @@ -134,7 +134,7 @@ size_overflow(void) /* Return A + B, exiting if the result would overflow either ptrdiff_t or size_t. */ -static ATTRIBUTE_PURE ptrdiff_t +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t sumsize(size_t a, size_t b) { #ifdef ckd_add diff --git a/zic.c b/zic.c index bd0cf4cd..4af0ca27 100644 --- a/zic.c +++ b/zic.c @@ -472,7 +472,7 @@ size_overflow(void) memory_exhausted(_("size overflow")); } -static ATTRIBUTE_PURE ptrdiff_t +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t size_sum(size_t a, size_t b) { #ifdef ckd_add @@ -487,7 +487,7 @@ size_sum(size_t a, size_t b) size_overflow(); } -static ATTRIBUTE_PURE ptrdiff_t +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t size_product(ptrdiff_t nitems, ptrdiff_t itemsize) { #ifdef ckd_mul @@ -502,7 +502,7 @@ size_product(ptrdiff_t nitems, ptrdiff_t itemsize) size_overflow(); } -static ATTRIBUTE_PURE ptrdiff_t +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t align_to(ptrdiff_t size, ptrdiff_t alignment) { ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits); @@ -3597,7 +3597,7 @@ lowerit(char a) } /* case-insensitive equality */ -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool ciequal(register const char *ap, register const char *bp) { while (lowerit(*ap) == lowerit(*bp++)) @@ -3606,7 +3606,7 @@ ciequal(register const char *ap, register const char *bp) return false; } -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool itsabbr(register const char *abbr, register const char *word) { if (lowerit(*abbr) != lowerit(*word)) @@ -3622,7 +3622,7 @@ itsabbr(register const char *abbr, register const char *word) /* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */ -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool ciprefix(char const *abbr, char const *word) { do @@ -3732,7 +3732,7 @@ time_overflow(void) exit(EXIT_FAILURE); } -static ATTRIBUTE_PURE zic_t +static ATTRIBUTE_REPRODUCIBLE zic_t oadd(zic_t t1, zic_t t2) { #ifdef ckd_add @@ -3746,7 +3746,7 @@ oadd(zic_t t1, zic_t t2) time_overflow(); } -static ATTRIBUTE_PURE zic_t +static ATTRIBUTE_REPRODUCIBLE zic_t tadd(zic_t t1, zic_t t2) { #ifdef ckd_add -- 2.37.2
* private.h (ATTRIBUTE_UNSEQUENCED): Rename from ATTRIBUTE_CONST, and use C23 style [[unsequenced]] if available. All uses changed. --- private.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/private.h b/private.h index 643068a7..4d667aa0 100644 --- a/private.h +++ b/private.h @@ -426,11 +426,9 @@ typedef unsigned long uintmax_t; #endif #if 3 <= __GNUC__ -# define ATTRIBUTE_CONST __attribute__((const)) # define ATTRIBUTE_MALLOC __attribute__((malloc)) # define ATTRIBUTE_FORMAT(spec) __attribute__((format spec)) #else -# define ATTRIBUTE_CONST /* empty */ # define ATTRIBUTE_MALLOC /* empty */ # define ATTRIBUTE_FORMAT(spec) /* empty */ #endif @@ -483,6 +481,19 @@ typedef unsigned long uintmax_t; # endif #endif +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(unsequenced) +# define ATTRIBUTE_UNSEQUENCED [[unsequenced]] +# endif +#endif +#ifndef ATTRIBUTE_UNSEQUENCED +# if 3 <= __GNUC__ +# define ATTRIBUTE_UNSEQUENCED __attribute__((const)) +# else +# define ATTRIBUTE_UNSEQUENCED /* empty */ +# endif +#endif + #if __STDC_VERSION__ < 199901 && !defined restrict # define restrict /* empty */ #endif @@ -604,7 +615,7 @@ char *asctime(struct tm const *); char *asctime_r(struct tm const *restrict, char *restrict); char *ctime(time_t const *); char *ctime_r(time_t const *, char *); -double difftime(time_t, time_t) ATTRIBUTE_CONST; +double difftime(time_t, time_t) ATTRIBUTE_UNSEQUENCED; size_t strftime(char *restrict, size_t, char const *restrict, struct tm const *restrict); # if HAVE_STRFTIME_L -- 2.37.2
Instead of informal /*fallthrough*/ comments, use C23 [[fallthrough]] if available. * Makefile (GCC_DEBUG_FLAGS): Add -Wimplicit-fallthrough=5. * private.h (ATTRIBUTE_FALLTHROUGH): New macro, now used whenever switch branches fall through. --- Makefile | 2 +- private.h | 13 +++++++++++++ zdump.c | 2 +- zic.c | 8 ++++---- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index b2c6ff6f..afb9d538 100644 --- a/Makefile +++ b/Makefile @@ -287,7 +287,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ -Wdeclaration-after-statement -Wdouble-promotion \ -Wduplicated-branches -Wduplicated-cond \ -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \ - -Winit-self -Wlogical-op \ + -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op \ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ -Wnull-dereference \ -Wold-style-definition -Woverlength-strings -Wpointer-arith \ diff --git a/private.h b/private.h index 4d667aa0..7a73eff7 100644 --- a/private.h +++ b/private.h @@ -440,6 +440,19 @@ typedef unsigned long uintmax_t; # define HAVE_HAS_C_ATTRIBUTE false #endif +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(fallthrough) +# define ATTRIBUTE_FALLTHROUGH [[fallthrough]] +# endif +#endif +#ifndef ATTRIBUTE_FALLTHROUGH +# if 7 <= __GNUC__ +# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough)) +# else +# define ATTRIBUTE_FALLTHROUGH ((void) 0) +# endif +#endif + #if HAVE_HAS_C_ATTRIBUTE # if __has_c_attribute(maybe_unused) # define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]] diff --git a/zdump.c b/zdump.c index 47141655..7acb3e2d 100644 --- a/zdump.c +++ b/zdump.c @@ -503,7 +503,7 @@ main(int argc, char *argv[]) case -1: if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) goto arg_processing_done; - /* Fall through. */ + ATTRIBUTE_FALLTHROUGH; default: usage(stderr, EXIT_FAILURE); } diff --git a/zic.c b/zic.c index 4af0ca27..892414af 100644 --- a/zic.c +++ b/zic.c @@ -1750,15 +1750,15 @@ gethms(char const *string, char const *errstring) default: ok = false; break; case 8: ok = '0' <= xr && xr <= '9'; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 7: ok &= ssx == '.'; if (ok && noise) warning(_("fractional seconds rejected by" " pre-2018 versions of zic")); - /* fallthrough */ - case 5: ok &= mmx == ':'; /* fallthrough */ - case 3: ok &= hhx == ':'; /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; + case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH; + case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH; case 1: break; } if (!ok) { -- 2.37.2
"have has" is a bit ugly.... Cheers, Philip On Mon, 21 Nov 2022 at 19:37, Paul Eggert via tz <tz@iana.org> wrote:
* private.h (__STDC_VERSION__): Default to 0. (ATTRIBUTE_MAYBE_UNUSED, ATTRIBUTE_NORETURN): Avoid diagnostic about use of [[...]] with 'gcc -std=c89 -pedantic'. --- private.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/private.h b/private.h index fa46094b..b9803427 100644 --- a/private.h +++ b/private.h @@ -17,6 +17,10 @@ ** Thank you! */
+#ifndef __STDC_VERSION__ +# define __STDC_VERSION__ 0 +#endif + /* Define true, false and bool if they don't work out of the box. */ #if __STDC_VERSION__ < 199901 # define true 1 @@ -433,7 +437,14 @@ typedef unsigned long uintmax_t; # define ATTRIBUTE_FORMAT(spec) /* empty */ #endif
-#ifdef __has_c_attribute +#if (defined __has_c_attribute \ + && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__)) +# define HAVE_HAS_C_ATTRIBUTE true +#else +# define HAVE_HAS_C_ATTRIBUTE false +#endif + +#if HAVE_HAS_C_ATTRIBUTE # if __has_c_attribute(maybe_unused) # define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]] # endif @@ -446,7 +457,7 @@ typedef unsigned long uintmax_t; # endif #endif
-#ifdef __has_c_attribute +#if HAVE_HAS_C_ATTRIBUTE # if __has_c_attribute(noreturn) # define ATTRIBUTE_NORETURN [[noreturn]] # endif -- 2.37.2
-- Philip Newton <philip.newton@gmail.com>
On 2022-11-28 05:40, Philip Newton wrote:
"have has" is a bit ugly....
Yes, I originally used the name "HAVE___HAS_C_ATTRIBUTE", since it tests whether __has_c_attribute exists and that's the naming convention, but I changed it to HAVE_HAS_C_ATTRIBUTE as I worried that the three adjacent underscores would be confusing. If people prefer HAVE___HAS_C_ATTRIBUTE we can easily change to that. The longer name would be more consistent.
participants (2)
-
Paul Eggert -
Philip Newton