[tz] [PROPOSED] Port to GCC 4.8.4

Paul Eggert eggert at cs.ucla.edu
Fri Jun 16 19:27:15 UTC 2017


Problem reported by Stan Shebs and Joseph Myers in:
https://sourceware.org/ml/libc-alpha/2017-06/msg00741.html
* NEWS, Makefile: Document this.
* private.h (HAVE_GENERIC): New macro, so that we do not
use _Generic with GCC 4.8.
(TIME_T_MIN_NO_PADDING, TIME_T_MAX_NO_PADDING): New macros.
(TIME_T_MIN, TIME_T_MAX): Rename from time_t_min and time_t_max,
and turn them into macros so that problems are less likely to
occur for subset uses like glibc where these macros are never
expanded.  All uses changed.  Use new macros to work around GCC
4.8 bug.
---
 Makefile    |  1 +
 NEWS        |  3 +++
 date.c      |  2 +-
 localtime.c | 26 +++++++++++++-------------
 private.h   | 60 ++++++++++++++++++++++++++++++++++++++++--------------------
 5 files changed, 58 insertions(+), 34 deletions(-)

diff --git a/Makefile b/Makefile
index 2b31f3f..347ece7 100644
--- a/Makefile
+++ b/Makefile
@@ -142,6 +142,7 @@ LDLIBS=
 #  -DHAVE_DECL_ASCTIME_R=0 if <time.h> does not declare asctime_r
 #  -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ'
 #  -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows)
+#  -DHAVE_GENERIC=0 if _Generic does not work
 #  -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)
 #  -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares
 #	ctime_r and asctime_r incompatibly with the POSIX standard
diff --git a/NEWS b/NEWS
index ca4fd8e..cfdd620 100644
--- a/NEWS
+++ b/NEWS
@@ -94,6 +94,9 @@ Unreleased, experimental changes
 
     zdump.c no longer assumes snprintf.  (Reported by Jonathan Leffler.)
 
+    Calculation of time_t extrema works around a bug in GCC 4.8.4
+    (Reported by Stan Shebs and Joseph Myers.)
+
     Several minor changes have been made to the code to make it a
     bit easier to port to MS-Windows and Solaris.  (Thanks to Kees
     Dekker for reporting the problems.)
diff --git a/date.c b/date.c
index b29e162..2cc533f 100644
--- a/date.c
+++ b/date.c
@@ -97,7 +97,7 @@ main(const int argc, char *argv[])
 			secs = strtoimax (optarg, &endarg, 0);
 			if (*endarg || optarg == endarg)
 				errno = EINVAL;
-			else if (! (time_t_min <= secs && secs <= time_t_max))
+			else if (! (TIME_T_MIN <= secs && secs <= TIME_T_MAX))
 				errno = ERANGE;
 			if (errno) {
 				perror(optarg);
diff --git a/localtime.c b/localtime.c
index 8c944f3..2e1d621 100644
--- a/localtime.c
+++ b/localtime.c
@@ -464,17 +464,17 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
 		sp->charcnt = charcnt;
 
 		/* Read transitions, discarding those out of time_t range.
-		   But pretend the last transition before time_t_min
-		   occurred at time_t_min.  */
+		   But pretend the last transition before TIME_T_MIN
+		   occurred at TIME_T_MIN.  */
 		timecnt = 0;
 		for (i = 0; i < sp->timecnt; ++i) {
 			int_fast64_t at
 			  = stored == 4 ? detzcode(p) : detzcode64(p);
-			sp->types[i] = at <= time_t_max;
+			sp->types[i] = at <= TIME_T_MAX;
 			if (sp->types[i]) {
 			  time_t attime
-			    = ((TYPE_SIGNED(time_t) ? at < time_t_min : at < 0)
-			       ? time_t_min : at);
+			    = ((TYPE_SIGNED(time_t) ? at < TIME_T_MIN : at < 0)
+			       ? TIME_T_MIN : at);
 			  if (timecnt && attime <= sp->ats[timecnt - 1]) {
 			    if (attime < sp->ats[timecnt - 1])
 			      return EINVAL;
@@ -524,7 +524,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
 		  /* Leap seconds cannot occur before the Epoch.  */
 		  if (tr < 0)
 		    return EINVAL;
-		  if (tr <= time_t_max) {
+		  if (tr <= TIME_T_MAX) {
 		    /* Leap seconds cannot occur more than once per UTC month,
 		       and UTC months are at least 28 days long (minus 1
 		       second for a negative leap second).  Each leap second's
@@ -1795,12 +1795,12 @@ increment_overflow_time(time_t *tp, int_fast32_t j)
 {
 	/*
 	** This is like
-	** 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...',
+	** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
 	** except that it does the right thing even if *tp + j would overflow.
 	*/
 	if (! (j < 0
-	       ? (TYPE_SIGNED(time_t) ? time_t_min - j <= *tp : -1 - j < *tp)
-	       : *tp <= time_t_max - j))
+	       ? (TYPE_SIGNED(time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
+	       : *tp <= TIME_T_MAX - j))
 		return true;
 	*tp += j;
 	return false;
@@ -1936,8 +1936,8 @@ time2sub(struct tm *const tmp,
 	/*
 	** Do a binary search (this works whatever time_t's type is).
 	*/
-	lo = time_t_min;
-	hi = time_t_max;
+	lo = TIME_T_MIN;
+	hi = TIME_T_MAX;
 	for ( ; ; ) {
 		t = lo / 2 + hi / 2;
 		if (t < lo)
@@ -1954,12 +1954,12 @@ time2sub(struct tm *const tmp,
 		} else	dir = tmcomp(&mytm, &yourtm);
 		if (dir != 0) {
 			if (t == lo) {
-				if (t == time_t_max)
+				if (t == TIME_T_MAX)
 					return WRONG;
 				++t;
 				++lo;
 			} else if (t == hi) {
-				if (t == time_t_min)
+				if (t == TIME_T_MIN)
 					return WRONG;
 				--t;
 				--hi;
diff --git a/private.h b/private.h
index 2b07251..f5e39b5 100644
--- a/private.h
+++ b/private.h
@@ -27,6 +27,21 @@
 #define HAVE_DECL_ASCTIME_R 1
 #endif
 
+#if !defined HAVE_GENERIC && defined __has_extension
+# if __has_extension(c_generic_selections)
+#  define HAVE_GENERIC 1
+# else
+#  define HAVE_GENERIC 0
+# endif
+#endif
+/* _Generic is buggy in pre-4.9 GCC.  */
+#if !defined HAVE_GENERIC && defined __GNUC__
+# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
+#endif
+#ifndef HAVE_GENERIC
+# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
+#endif
+
 #ifndef HAVE_GETTEXT
 #define HAVE_GETTEXT		0
 #endif /* !defined HAVE_GETTEXT */
@@ -561,27 +576,32 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
 #define MINVAL(t, b)						\
   ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
 
-/* The minimum and maximum finite time values.  This implementation
-   assumes no padding if time_t is signed and either the compiler is
-   pre-C11 or time_t is not one of the standard signed integer types.  */
-#if 201112 <= __STDC_VERSION__
-static time_t const time_t_min
-  = (TYPE_SIGNED(time_t)
-     ? _Generic((time_t) 0,
-		signed char: SCHAR_MIN, short: SHRT_MIN,
-		int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN,
-		default: MINVAL(time_t, TYPE_BIT(time_t)))
-     : 0);
-static time_t const time_t_max
-  = (TYPE_SIGNED(time_t)
-     ? _Generic((time_t) 0,
-		signed char: SCHAR_MAX, short: SHRT_MAX,
-		int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX,
-		default: MAXVAL(time_t, TYPE_BIT(time_t)))
-     : -1);
+/* The extreme time values, assuming no padding.  */
+#define TIME_T_MIN_NO_PADDING MINVAL(time_t, TYPE_BIT(time_t))
+#define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t))
+
+/* The extreme time values.  These are macros, not constants, so that
+   any portability problem occur only when compiling .c files that use
+   the macros, which is safer for applications that need only zdump and zic.
+   This implementation assumes no padding if time_t is signed and
+   either the compiler lacks support for _Generic or time_t is not one
+   of the standard signed integer types.  */
+#if HAVE_GENERIC
+# define TIME_T_MIN \
+    _Generic((time_t) 0, \
+	     signed char: SCHAR_MIN, short: SHRT_MIN, \
+	     int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \
+	     default: TIME_T_MIN_NO_PADDING)
+# define TIME_T_MAX \
+    (TYPE_SIGNED(time_t) \
+     ? _Generic((time_t) 0, \
+		signed char: SCHAR_MAX, short: SHRT_MAX, \
+		int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
+		default: TIME_T_MAX_NO_PADDING)			    \
+     : (time_t) -1)
 #else
-static time_t const time_t_min = MINVAL(time_t, TYPE_BIT(time_t));
-static time_t const time_t_max = MAXVAL(time_t, TYPE_BIT(time_t));
+# define TIME_T_MIN TIME_T_MIN_NO_PADDING
+# define TIME_T_MAX TIME_T_MAX_NO_PADDING
 #endif
 
 /*
-- 
2.9.4



More information about the tz mailing list