From 71dedb336f2cbcfa71ca3ed2d153ebd4eb6e9389 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Wed, 1 Mar 2017 23:39:59 +0000 Subject: [PATCH] PR middle-end/79692 - [7 Regression] -Wformat-overflow false positive gcc/ChangeLog: PR middle-end/79692 * gimple-ssa-sprintf.c (directive::known_width_and_precision): New function. (format_integer): Use it. (get_mpfr_format_length): Consider the full range of precision when computing %g output with the # flag. Set the likely byte count to 3 rather than 1 when precision is indeterminate. (format_floating): Correct the lower bound of precision. gcc/testsuite/ChangeLog: PR middle-end/79692 * gcc.dg/tree-ssa/builtin-sprintf-2.c: Add test cases. * gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: Correct %#g. * gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: New test. * gcc.dg/tree-ssa/builtin-snprintf-3.c: Ditto. From-SVN: r245822 --- gcc/ChangeLog | 11 + gcc/gimple-ssa-sprintf.c | 164 ++++++++++++--- gcc/testsuite/ChangeLog | 8 + .../gcc.dg/tree-ssa/builtin-snprintf-3.c | 77 +++++++ .../gcc.dg/tree-ssa/builtin-sprintf-2.c | 14 +- .../gcc.dg/tree-ssa/builtin-sprintf-warn-10.c | 8 +- .../gcc.dg/tree-ssa/builtin-sprintf-warn-15.c | 197 ++++++++++++++++++ 7 files changed, 441 insertions(+), 38 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-3.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3580685fb5f..4f440866081 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2017-03-01 Martin Sebor + + PR middle-end/79692 + * gimple-ssa-sprintf.c + (directive::known_width_and_precision): New function. + (format_integer): Use it. + (get_mpfr_format_length): Consider the full range of precision + when computing %g output with the # flag. Set the likely byte + count to 3 rather than 1 when precision is indeterminate. + (format_floating): Correct the lower bound of precision. + 2017-03-01 Bill Schmidt * doc/invoke.texi: Document default code model for 64-bit Linux. diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 7688439aab7..0448b2127be 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -692,6 +692,16 @@ struct directive { get_int_range (arg, integer_type_node, prec, prec + 1, false, -1); } + + /* Return true if both width and precision are known to be + either constant or in some range, false otherwise. */ + bool known_width_and_precision () const + { + return ((width[1] < 0 + || (unsigned HOST_WIDE_INT)width[1] <= target_int_max ()) + && (prec[1] < 0 + || (unsigned HOST_WIDE_INT)prec[1] < target_int_max ())); + } }; /* Return the logarithm of X in BASE. */ @@ -1180,10 +1190,10 @@ format_integer (const directive &dir, tree arg) /* As a special case, a precision of zero with a zero argument results in zero bytes except in base 8 when the '#' flag is specified, and for signed conversions in base 8 and 10 when - flags when either the space or '+' flag has been specified - when it results in just one byte (with width having the normal - effect). This must extend to the case of a specified precision - with an unknown value because it can be zero. */ + either the space or '+' flag has been specified and it results + in just one byte (with width having the normal effect). This + must extend to the case of a specified precision with + an unknown value because it can be zero. */ res.range.min = ((base == 8 && dir.get_flag ('#')) || maybesign); if (res.range.min == 0 && dir.prec[0] != dir.prec[1]) { @@ -1254,10 +1264,12 @@ format_integer (const directive &dir, tree arg) argmax = wide_int_to_tree (argtype, max); /* Set KNOWNRANGE if the argument is in a known subrange - of the directive's type (KNOWNRANGE may be reset below). */ + of the directive's type and neither width nor precision + is unknown. (KNOWNRANGE may be reset below). */ res.knownrange - = (!tree_int_cst_equal (TYPE_MIN_VALUE (dirtype), argmin) - || !tree_int_cst_equal (TYPE_MAX_VALUE (dirtype), argmax)); + = ((!tree_int_cst_equal (TYPE_MIN_VALUE (dirtype), argmin) + || !tree_int_cst_equal (TYPE_MAX_VALUE (dirtype), argmax)) + && dir.known_width_and_precision ()); res.argmin = argmin; res.argmax = argmax; @@ -1421,12 +1433,12 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec, HOST_WIDE_INT p = prec; - if (spec == 'G') + if (spec == 'G' && !strchr (flags, '#')) { - /* For G/g, precision gives the maximum number of significant - digits which is bounded by LDBL_MAX_10_EXP, or, for a 128 - bit IEEE extended precision, 4932. Using twice as much - here should be more than sufficient for any real format. */ + /* For G/g without the pound flag, precision gives the maximum number + of significant digits which is bounded by LDBL_MAX_10_EXP, or, for + a 128 bit IEEE extended precision, 4932. Using twice as much here + should be more than sufficient for any real format. */ if ((IEEE_MAX_10_EXP * 2) < prec) prec = IEEE_MAX_10_EXP * 2; p = prec; @@ -1609,7 +1621,12 @@ format_floating (const directive &dir) /* Compute the upper bound for -TYPE_MAX. */ res.range.max = format_floating_max (type, 'f', dir.prec[1]); - res.range.likely = res.range.min; + /* The minimum output with unknown precision is a single byte + (e.g., "0") but the more likely output is 3 bytes ("0.0"). */ + if (dir.prec[0] < 0 && dir.prec[1] > 0) + res.range.likely = 3; + else + res.range.likely = res.range.min; /* The unlikely maximum accounts for the longest multibyte decimal point character. */ @@ -1625,10 +1642,43 @@ format_floating (const directive &dir) /* The %g output depends on precision and the exponent of the argument. Since the value of the argument isn't known the lower bound on the range of bytes (not counting flags - or width) is 1. */ - res.range.min = flagmin; - res.range.max = format_floating_max (type, 'g', dir.prec[1]); - res.range.likely = res.range.max; + or width) is 1 plus radix (i.e., either "0" or "0." for + "%g" and "%#g", respectively, with a zero argument). */ + res.range.min = flagmin + radix; + + char spec = 'g'; + HOST_WIDE_INT maxprec = dir.prec[1]; + if (radix && maxprec) + { + /* When the pound flag (radix) is set, trailing zeros aren't + trimmed and so the longest output is the same as for %e, + except with precision minus 1 (as specified in C11). */ + spec = 'e'; + if (maxprec > 0) + --maxprec; + else if (maxprec < 0) + maxprec = 5; + } + + res.range.max = format_floating_max (type, spec, maxprec); + + /* The likely output is either the maximum computed above + minus 1 (assuming the maximum is positive) when precision + is known (or unspecified), or the same minimum as for %e + (which is computed for a non-negative argument). Unlike + for the other specifiers above the likely output isn't + the minimum because for %g that's 1 which is unlikely. */ + if (dir.prec[1] < 0 + || (unsigned HOST_WIDE_INT)dir.prec[1] < target_int_max ()) + res.range.likely = res.range.max - 1; + else + { + HOST_WIDE_INT minprec = 6 + !radix /* decimal point */; + res.range.likely = (flagmin + + radix + + minprec + + 2 /* e+ */ + 2); + } /* The unlikely maximum accounts for the longest multibyte decimal point character. */ @@ -1657,24 +1707,63 @@ format_floating (const directive &dir, tree arg) HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] }; + /* For an indeterminate precision the lower bound must be assumed + to be zero. */ if (TOUPPER (dir.specifier) == 'A') { + /* Get the number of fractional decimal digits needed to represent + the argument without a loss of accuracy. */ + tree type = arg ? TREE_TYPE (arg) : + (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll + ? long_double_type_node : double_type_node); + + unsigned fmtprec + = REAL_MODE_FORMAT (TYPE_MODE (type))->p; + + /* The precision of the IEEE 754 double format is 53. + The precision of all other GCC binary double formats + is 56 or less. */ + unsigned maxprec = fmtprec <= 56 ? 13 : 15; + /* For %a, leave the minimum precision unspecified to let MFPR trim trailing zeros (as it and many other systems including Glibc happen to do) and set the maximum precision to reflect what it would be with trailing zeros present (as Solaris and derived systems do). */ - if (prec[0] < 0) - prec[0] = -1; - if (prec[1] < 0) + if (dir.prec[1] < 0) { - unsigned fmtprec - = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->p; - - /* The precision of the IEEE 754 double format is 53. - The precision of all other GCC binary double formats - is 56 or less. */ - prec[1] = fmtprec <= 56 ? 13 : 15; + /* Both bounds are negative implies that precision has + not been specified. */ + prec[0] = maxprec; + prec[1] = -1; + } + else if (dir.prec[0] < 0) + { + /* With a negative lower bound and a non-negative upper + bound set the minimum precision to zero and the maximum + to the greater of the maximum precision (i.e., with + trailing zeros present) and the specified upper bound. */ + prec[0] = 0; + prec[1] = dir.prec[1] < maxprec ? maxprec : dir.prec[1]; + } + } + else if (dir.prec[0] < 0) + { + if (dir.prec[1] < 0) + { + /* A precision in a strictly negative range is ignored and + the default of 6 is used instead. */ + prec[0] = prec[1] = 6; + } + else + { + /* For a precision in a partly negative range, the lower bound + must be assumed to be zero and the new upper bound is the + greater of 6 (the default precision used when the specified + precision is negative) and the upper bound of the specified + range. */ + prec[0] = 0; + prec[1] = dir.prec[1] < 6 ? 6 : dir.prec[1]; } } @@ -1734,12 +1823,23 @@ format_floating (const directive &dir, tree arg) res.range.max = tmp; } - res.knownrange = true; + /* The range is known unless either width or precision is unknown. */ + res.knownrange = dir.known_width_and_precision (); + + /* For the same floating point constant, unless width or precision + is unknown, use the longer output as the likely maximum since + with round to nearest either is equally likely. Otheriwse, when + precision is unknown, use the greater of the minimum and 3 as + the likely output (for "0.0" since zero precision is unlikely). */ + if (res.knownrange) + res.range.likely = res.range.max; + else if (res.range.min < 3 + && dir.prec[0] < 0 + && (unsigned HOST_WIDE_INT)dir.prec[1] == target_int_max ()) + res.range.likely = 3; + else + res.range.likely = res.range.min; - /* For the same floating point constant use the longer output - as the likely maximum since with round to nearest either is - equally likely. */ - res.range.likely = res.range.max; res.range.unlikely = res.range.max; if (res.range.max > 2 && (prec[0] != 0 || prec[1] != 0)) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2b73362781c..59264c2d4ef 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2017-03-01 Martin Sebor + + PR middle-end/79692 + * gcc.dg/tree-ssa/builtin-sprintf-2.c: Add test cases. + * gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: Correct %#g. + * gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: New test. + * gcc.dg/tree-ssa/builtin-snprintf-3.c: Ditto. + 2017-03-01 Uros Bizjak * gcc.target/i386/invsize-2.c: New test. diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-3.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-3.c new file mode 100644 index 00000000000..e481955ab73 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-3.c @@ -0,0 +1,77 @@ +/* Verify the lower and upper bounds of floating directives with + precision whose range crosses zero. + { do-do compile } + { dg-options "-O2 -Wall -fdump-tree-optimized" } */ + +static const double x = 1.23456789; + +/* All calls to failure_range must be eliminated. */ +extern void failure_range (int, int, int); + +/* All calls to verify_{lo,hi}_bound must be retained. */ +extern void verify_lo_bound (int, int); +extern void verify_hi_bound (int, int); + +int test_a (int p) +{ + if (p < -1 || 3 < p) + p = -1; + + int n = __builtin_snprintf (0, 0, "%.*A", p, x); + if (n < 6 || 25 < n) + failure_range ('A', 6, 25); + + if (n == 6) verify_lo_bound ('A', 6); + if (n == 25) verify_hi_bound ('A', 25); + + return n; +} + +int test_e (int p) +{ + if (p < -1 || 3 < p) + p = -1; + + int n = __builtin_snprintf (0, 0, "%.*E", p, x); + if (n < 5 || 17 < n) + failure_range ('E', 5, 17); + + if (n == 5) verify_lo_bound ('E', 5); + if (n == 17) verify_hi_bound ('E', 17); + + return n; +} + +int test_f (int p) +{ + if (p < -1 || 3 < p) + p = -1; + + int n = __builtin_snprintf (0, 0, "%.*F", p, x); + if (n < 1 || 13 < n) + failure_range ('F', 1, 13); + + if (n == 1) verify_lo_bound ('F', 1); + if (n == 13) verify_hi_bound ('F', 13); + + return n; +} + +int test_g (int p) +{ + if (p < -1 || 3 < p) + p = -1; + + int n = __builtin_snprintf (0, 0, "%.*G", p, x); + if (n < 1 || 12 < n) + failure_range ('G', 1, 12); + + if (n == 1) verify_lo_bound ('G', 1); + if (n == 12) verify_hi_bound ('G', 12); + + return n; +} + +/* { dg-final { scan-tree-dump-times "snprintf" 4 "optimized"} } + { dg-final { scan-tree-dump-not "failure_range" "optimized"} } + { dg-final { scan-tree-dump-times "verify_" 8 "optimized"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c index b873a0c443c..8a13f33d2a1 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c @@ -7,7 +7,7 @@ The test is compiled with warnings disabled to make sure the absence of optimizations does not depend on the presence of warnings. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized -ftrack-macro-expansion=0 -w" } */ +/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized -w" } */ #ifndef LINE # define LINE 0 @@ -243,6 +243,14 @@ RNG (6, 6, 7, "%La", 0.0L) /* Glibc output: "0x0p+0" */ RNG (6, 6, 7, "%La", ld) RNG (6, 6, 7, "%.4096La", ld) +/* Verify that the pound flag with unknown precision prevents the %g + directive from trimming trailing zeros as it otherwise does. As + a consequence, the result must be assumed to be as large as + precision. */ +RNG (1, 315, 316, "%#.*g", i, d); +RNG (1, 4095, 4096, "%#.*g", i, d); +RNG (1, 4095, 4096, "%#.*g", i, 0.0); + /* Verify that the result of formatting an unknown string isn't optimized into a non-negative range. The string could be longer that 4,095 bytes, resulting in the formatting function having undefined behavior (and @@ -282,7 +290,7 @@ RNG (0, 6, 8, "%s%ls", "1", L"2"); /* Only conditional calls to must_not_eliminate must be made (with any probability): - { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 124 "optimized" { target { ilp32 || lp64 } } } } - { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } + { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 127 "optimized" { target { ilp32 || lp64 } } } } + { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 96 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } No unconditional calls to abort should be made: { dg-final { scan-tree-dump-not ";\n *must_not_eliminate" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-10.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-10.c index 5523acd7db1..1213e89f7bb 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-10.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-10.c @@ -239,9 +239,11 @@ void test_g_va (va_list va) T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */ T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */ T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */ - T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */ - T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */ - T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */ + + /* The pound flag means the radix character is always present. */ + T ("%#g"); /* { dg-warning "between 2 and 13 bytes" } */ + T ("%#+g"); /* { dg-warning "between 3 and 13 bytes" } */ + T ("%# g"); /* { dg-warning "between 3 and 13 bytes" } */ T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */ T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c new file mode 100644 index 00000000000..c38a656bc7b --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c @@ -0,0 +1,197 @@ +/* PR middle-end/79692 - -Wformat-overflow false positive on an integer + directive with unknown width + { dg-do compile } + { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } + { dg-require-effective-target int32plus } */ + +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) + +/* When debugging, define LINE to the line number of the test case to exercise + and avoid exercising any of the others. The buffer and objsize macros + below make use of LINE to avoid warnings for other lines. */ +#ifndef LINE +# define LINE 0 +#endif + +void sink (char*, char*); + +int dummy_sprintf (char*, const char*, ...); + +char buffer [1024]; +extern char *ptr; + +int int_range (int min, int max) +{ + extern int int_value (void); + int n = int_value (); + return n < min || max < n ? min : n; +} + +unsigned uint_range (unsigned min, unsigned max) +{ + extern unsigned uint_value (void); + unsigned n = uint_value (); + return n < min || max < n ? min : n; +} + +/* Evaluate to an array of SIZE characters when non-negative, or to + a pointer to an unknown object otherwise. */ +#define buffer(size) \ + ((0 <= size) ? buffer + sizeof buffer - (size) : ptr) + +/* Helper to expand function to either __builtin_f or dummy_f to + make debugging GCC easy. */ +#define FUNC(f) \ + ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f) + +/* Macro to verify that calls to __builtin_sprintf (i.e., with no size + argument) issue diagnostics by correctly determining the size of + the destination buffer. */ +#define T(size, ...) \ + (FUNC (sprintf) (buffer (size), __VA_ARGS__), \ + sink (buffer, ptr)) + +/* Return a signed integer in the range [MIN, MAX]. */ +#define R(min, max) int_range (min, max) + +void test_unknown_width_integer (int w, int i) +{ + T (10, "%*d", w, i); + T (10, "%*d", w, R (0, 12345)); + + T (10, "%*i", w, i); + T (10, "%*i", w, R (0, 12345)); + + T (10, "%*o", w, i); + T (10, "%*o", w, R (0, 12345)); + + T (10, "%*i", w, i); + T (10, "%*i", w, R (0, 12345)); +} + +void test_unknown_width_floating (int w, double d) +{ + T ( 7, "%*a", w, d); + T (21, "%*a", w, 3.141); + + T (12, "%*e", w, d); /* { dg-warning "writing a terminating nul" } */ + T (12, "%#*e", w, d); /* { dg-warning "writing a terminating nul" } */ + T (13, "%*e", w, d); + T (13, "%#*e", w, d); + T (13, "%*e", w, 3.141); + + T ( 8, "%*f", w, d); /* { dg-warning "writing a terminating nul" } */ + T ( 8, "%#*f", w, d); /* { dg-warning "writing a terminating nul" } */ + T ( 9, "%*f", w, d); + T ( 9, "%#*f", w, d); + T ( 9, "%*f", w, 3.141); + T ( 9, "%#*f", w, 3.141); + + T (12, "%*g", w, d); /* { dg-warning "may write a terminating nul" } */ + T (13, "%*g", w, d); + T (13, "%*g", w, 3.141); +} + +void test_unknown_precision_integer (int p, int i, double d) +{ + T (10, "%.*d", p, i); + T (10, "%.*d", p, R (0, 12345)); + + T (10, "%.*i", p, i); + T (10, "%.*i", p, R (0, 12345)); + + T (10, "%.*o", p, i); + T (10, "%.*o", p, R (0, 12345)); + + T (10, "%.*i", p, i); + T (10, "%.*i", p, R (0, 12345)); +} + +void test_unknown_precision_floating (int p, double d) +{ + T ( 7, "%.*a", p, d); + T (21, "%.*a", p, 3.141); + + /* "%.0e", 0.0 results in 5 bytes: "0e+00" */ + T ( 5, "%.*e", p, d); /* { dg-warning "writing a terminating nul" } */ + /* "%#.0e", 0.0 results in 6 bytes: "0.e+00" */ + T ( 6, "%#.*e", p, d); /* { dg-warning "writing a terminating nul" } */ + T ( 6, "%.*e", p, d); + T ( 6, "%.*e", p, 3.141); + T ( 6, "%#.*e", p, 3.141); /* { dg-warning "writing a terminating nul" } */ + T ( 7, "%#.*e", p, 3.141); + + /* "%.0f", 0.0 results in 1 byte: "0" but precision of at least 1 + is likely, resulting in "0.0". */ + T ( 3, "%.*f", p, d); /* { dg-warning "may write a terminating nul" } */ + /* "%#.0f", 0.0 results in 2 bytes: "0." but precision of at least 1 + is likely, resulting in "0.0". */ + T ( 3, "%#.*f", p, d); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%.*f", p, d); + T ( 4, "%#.*f", p, d); + T ( 3, "%.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%.*f", p, 3.141); + T ( 3, "%#.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%#.*f", p, 3.141); + + T (12, "%.*g", p, d); /* { dg-warning "may write a terminating nul" } */ + T (12, "%#.*g", p, d); /* { dg-warning "may write a terminating nul" } */ + T (13, "%.*g", p, d); + T (13, "%#.*g", p, d); + T ( 6, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */ + T ( 7, "%#.*g", R (-1, 0), d); + T ( 6, "%#.*g", R ( 0, 0), d);/* { dg-warning "may write a terminating nul" } */ + T ( 7, "%#.*g", R ( 0, 0), d); + T ( 6, "%#.*g", R ( 0, 1), d);/* { dg-warning "may write a terminating nul" } */ + T ( 7, "%#.*g", R ( 0, 1), d); + T ( 3, "%.*g", p, 3.141); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%.*g", p, 3.141); + T ( 3, "%#.*g", p, 3.141); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%#.*g", p, 3.141); +} + + +void test_unknown_width_and_precision_integer (int w, int p, int i) +{ + T (10, "%*.*d", w, p, i); + T (10, "%*.*d", w, p, R (0, 12345)); + + T (10, "%*.*i", w, p, i); + T (10, "%*.*i", w, p, R (0, 12345)); + + T (10, "%*.*o", w, p, i); + T (10, "%*.*o", w, p, R (0, 12345)); + + T (10, "%*.*i", w, p, i); + T (10, "%*.*i", w, p, R (0, 12345)); +} + +void test_unknown_width_and_precision_floating (int w, int p, double d) +{ + T ( 7, "%*.*a", w, p, d); + T (21, "%*.*a", w, p, 3.141); + + /* "%0.0e", 0.0 results in 5 bytes: "0e+00" */ + T ( 5, "%*.*e", w, p, d); /* { dg-warning "writing a terminating nul" } */ + /* "%#0.0e", 0.0 results in 6 bytes: "0.e+00" */ + T ( 6, "%#*.*e", w, p, d); /* { dg-warning "writing a terminating nul" } */ + T ( 6, "%*.*e", w, p, d); + T ( 6, "%*.*e", w, p, 3.141); + T ( 6, "%#*.*e", w, p, 3.141);/* { dg-warning "writing a terminating nul" } */ + T ( 7, "%#*.*e", w, p, 3.141); + + T ( 3, "%*.*f", w, p, d); /* { dg-warning "may write a terminating nul" } */ + T ( 3, "%#*.*f", w, p, d); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%*.*f", w, p, d); + T ( 4, "%*.*f", w, p, 3.141); + T ( 4, "%#*.*f", w, p, 3.141); + + T (13, "%*.*g", w, p, d); + T (13, "%#*.*g", w, p, d); + T (13, "%*.*g", w, p, 3.141); + T (13, "%#*.*g", w, p, 3.141); +}