PR tree-optimization/78608 - gimple-ssa-sprintf.c:570:17: runtime error: negation of -9223372036854775808 cannot be represented in type 'long int'
gcc/ChangeLog:
PR tree-optimization/78608
* gimple-ssa-sprintf.c (tree_digits): Avoid negating a minimum signed
value.
(get_width_and_precision): Same.
(format_integer): Use HOST_WIDE_INT for expected sprintf return value
to allow for width being the absolte value of INT_MIN.
(format_floating): Use HOST_WIDE_INT for width and precision. Store
argument type for use in diagnostics. Use target INT_MAX rather than
the host constant.
(format_floating): Reuse get_width_and_precision instead of hadcoding
the same.
(maybe_get_value_range_from_type): New function.
(format_directive): Treat INT_MAX + 1 as a valid (if excessive) byte
count. Call maybe_get_value_range_from_type.
gcc/testsuite/ChangeLog:
PR tree-optimization/78608
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Add test cases.
===================================================================
@@ -565,8 +565,14 @@ tree_digits (tree x, int base, bool plus, bool pre
if (tree_fits_shwi_p (x))
{
HOST_WIDE_INT i = tree_to_shwi (x);
- if (i < 0)
+ if (HOST_WIDE_INT_MIN == i)
{
+ /* Avoid undefined behavior due to negating a minimum. */
+ absval = HOST_WIDE_INT_MAX;
+ res = 1;
+ }
+ else if (i < 0)
+ {
absval = -i;
res = 1;
}
@@ -774,7 +780,23 @@ get_width_and_precision (const conversion_spec &sp
if (spec.star_width)
{
if (TREE_CODE (spec.star_width) == INTEGER_CST)
- width = abs (tree_to_shwi (spec.star_width));
+ {
+ width = tree_to_shwi (spec.star_width);
+ if (width < 0)
+ {
+ if (width == HOST_WIDE_INT_MIN)
+ {
+ /* Avoid undefined behavior due to negating a minimum.
+ This case will be diagnosed since it will result in
+ more than INT_MAX bytes on output, either by the
+ directive itself (when INT_MAX < HOST_WIDE_INT_MAX)
+ or by the format function itself. */
+ width = HOST_WIDE_INT_MAX;
+ }
+ else
+ width = -width;
+ }
+ }
else
width = HOST_WIDE_INT_MIN;
}
@@ -913,7 +935,7 @@ format_integer (const conversion_spec &spec, tree
gcc_unreachable ();
}
- int len;
+ HOST_WIDE_INT len;
if ((prec == HOST_WIDE_INT_MIN || prec == 0) && integer_zerop (arg))
{
@@ -1118,8 +1140,8 @@ format_integer (const conversion_spec &spec, tree
return res;
}
-/* Return the number of bytes to format using the format specifier
- SPEC the largest value in the real floating TYPE. */
+/* Return the number of bytes to format, using the format specifier SPEC
+ and precision PREC, the largest value in the real floating TYPE. */
static int
format_floating_max (tree type, char spec, int prec = -1)
@@ -1170,7 +1192,8 @@ format_floating_max (tree type, char spec, int pre
is used when the directive argument or its value isn't known. */
static fmtresult
-format_floating (const conversion_spec &spec, int width, int prec)
+format_floating (const conversion_spec &spec,
+ HOST_WIDE_INT width, HOST_WIDE_INT prec)
{
tree type;
bool ldbl = false;
@@ -1214,6 +1237,13 @@ static fmtresult
logexpdigs = ilog (expdigs, 10);
}
+ /* Store only the type in RES.ARGMIN. If a diagnostic needs to be
+ issued for the directive the range of values used to determine
+ the number of bytes for it will be computed at that time. This
+ avoids building the real constants corresponding to the range
+ for every directive even if no diagnostic is issued. */
+ res.argmin = type;
+
switch (spec.specifier)
{
case 'A':
@@ -1221,13 +1251,15 @@ static fmtresult
{
/* The minimum output is "0x.p+0". */
res.range.min = 6 + (prec > 0 ? prec : 0);
- res.range.max = (width == INT_MIN
- ? HOST_WIDE_INT_MAX
+ /* The maximum is the greater of widh and the number of bytes
+ produced by formatting -TYPE_MAX. When width is unknown,
+ the maximum is -INT_MIN (this is undefined and diagnosed). */
+ res.range.max = (width == HOST_WIDE_INT_MIN
+ ? target_int_max () + 1
: format_floating_max (type, 'a', prec));
-
/* The output of "%a" is fully specified only when precision
is explicitly specified and width isn't unknown. */
- res.bounded = INT_MIN != width && -1 < prec;
+ res.bounded = HOST_WIDE_INT_MIN != width && -1 < prec;
break;
}
@@ -1243,13 +1275,13 @@ static fmtresult
/* Unless width is uknown the maximum output is the minimum plus
sign (unless already included), plus the difference between
the minimum exponent of 2 and the maximum exponent for the type. */
- res.range.max = (width == INT_MIN
- ? HOST_WIDE_INT_M1U
+ res.range.max = (width == HOST_WIDE_INT_MIN
+ ? target_int_max () + 1
: res.range.min + !sign + logexpdigs - 2);
/* "%e" is fully specified and the range of bytes is bounded
unless width is unknown. */
- res.bounded = INT_MIN != width;
+ res.bounded = HOST_WIDE_INT_MIN != width;
break;
}
@@ -1259,17 +1291,18 @@ static fmtresult
/* The minimum output is "1.234567" regardless of the value
of the actual argument. */
res.range.min = 2 + (prec < 0 ? 6 : prec);
-
- /* Compute the maximum just once. */
+
+ /* As an optimization, compute the maximum just once. */
static const int f_max[] = {
format_floating_max (double_type_node, 'f'),
format_floating_max (long_double_type_node, 'f')
};
- res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl];
+ res.range.max = (width == HOST_WIDE_INT_MIN
+ ? target_int_max () + 1 : f_max[ldbl]);
/* "%f" is fully specified and the range of bytes is bounded
unless width is unknown. */
- res.bounded = INT_MIN != width;
+ res.bounded = HOST_WIDE_INT_MIN != width;
break;
}
case 'G':
@@ -1278,16 +1311,17 @@ static fmtresult
/* The minimum is the same as for '%F'. */
res.range.min = 2 + (prec < 0 ? 6 : prec);
- /* Compute the maximum just once. */
+ /* As an optimization, compute the maximum just once. */
static const int g_max[] = {
format_floating_max (double_type_node, 'g'),
format_floating_max (long_double_type_node, 'g')
};
- res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl];
+ res.range.max = (width == HOST_WIDE_INT_MIN
+ ? HOST_WIDE_INT_MAX : g_max[ldbl]);
/* "%g" is fully specified and the range of bytes is bounded
unless width is unknown. */
- res.bounded = INT_MIN != width;
+ res.bounded = HOST_WIDE_INT_MIN != width;
break;
}
@@ -1316,42 +1350,23 @@ format_floating (const conversion_spec &spec, tree
/* Set WIDTH to -1 when it's not specified, to INT_MIN when it is
specified by the asterisk to an unknown value, and otherwise to
a non-negative value corresponding to the specified width. */
- int width = -1;
- int prec = -1;
+ HOST_WIDE_INT width;
+ HOST_WIDE_INT prec;
+ get_width_and_precision (spec, &width, &prec);
+ /* FIXME: Handle specified precision with an unknown value. */
+ if (prec == HOST_WIDE_INT_MIN)
+ return fmtresult ();
+
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res;
res.constant = arg && TREE_CODE (arg) == REAL_CST;
- if (spec.have_width)
- width = spec.width;
- else if (spec.star_width)
+ if (res.constant
+ && !spec.have_precision
+ && !spec.star_precision
+ && TOUPPER (spec.specifier) != 'A')
{
- if (TREE_CODE (spec.star_width) == INTEGER_CST)
- {
- width = tree_to_shwi (spec.star_width);
- if (width < 0)
- width = -width;
- }
- else
- width = INT_MIN;
- }
-
- if (spec.have_precision)
- prec = spec.precision;
- else if (spec.star_precision)
- {
- if (TREE_CODE (spec.star_precision) == INTEGER_CST)
- prec = tree_to_shwi (spec.star_precision);
- else
- {
- /* FIXME: Handle non-constant precision. */
- res.range.min = res.range.max = HOST_WIDE_INT_M1U;
- return res;
- }
- }
- else if (res.constant && TOUPPER (spec.specifier) != 'A')
- {
/* Specify the precision explicitly since mpfr_sprintf defaults
to zero. */
prec = 6;
@@ -1384,11 +1399,9 @@ format_floating (const conversion_spec &spec, tree
if (spec.get_flag (*pf))
*pfmt++ = *pf;
- /* Append width when specified and precision. */
- if (-1 < width)
- pfmt += sprintf (pfmt, "%i", width);
+ /* Append precision but not width. */
if (-1 < prec)
- pfmt += sprintf (pfmt, ".%i", prec);
+ pfmt += sprintf (pfmt, ".%i", (int)prec);
/* Append the MPFR 'R' floating type specifier (no length modifier
is necessary or allowed by MPFR for mpfr_t values). */
@@ -1412,10 +1425,14 @@ format_floating (const conversion_spec &spec, tree
/* Format it and store the result in the corresponding
member of the result struct. */
*minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval);
+
+ /* Adjust the count if it's less than the specified width. */
+ if (0 < width && *minmax[i] < (unsigned HOST_WIDE_INT)width)
+ *minmax[i] = width;
}
/* The range of output is known even if the result isn't bounded. */
- if (width == INT_MIN)
+ if (width == HOST_WIDE_INT_MIN)
{
res.knownrange = false;
res.range.max = HOST_WIDE_INT_MAX;
@@ -1684,6 +1701,44 @@ format_string (const conversion_spec &spec, tree a
return res;
}
+/* Helper to construct tree nodes corresponding to the minimum and
+ maxium values for a type. When *ARGMIN is a real floating type
+ set [*ARGMIN, *ARGMAX] to the range of minimum and maximum values
+ of the type. Otherwise do nothing. */
+
+static void
+maybe_get_value_range_from_type (tree *argmin, tree *argmax)
+{
+ if (TREE_CODE (*argmin) != REAL_TYPE)
+ return;
+
+ /* *ARGMIN is actually a type. */
+ tree type = *argmin;
+
+ machine_mode mode = TYPE_MODE (type);
+
+ /* IBM Extended mode. */
+ if (MODE_COMPOSITE_P (mode))
+ mode = DFmode;
+
+ /* Get the real type format desription for the target. */
+ const real_format *rfmt = REAL_MODE_FORMAT (mode);
+ REAL_VALUE_TYPE rv;
+
+ {
+ /* Create a TYPE_MAX. */
+ char buf[256];
+ get_max_float (rfmt, buf, sizeof buf);
+ real_from_string (&rv, buf);
+ }
+
+ *argmax = build_real (type,
+ real_value_from_int_cst (type, integer_zero_node));
+
+ /* Create a TYPE_MIN by negating TYPE_MAX. */
+ *argmin = fold_build1 (NEGATE_EXPR, type, build_real (type, rv));
+}
+
/* Compute the length of the output resulting from the conversion
specification SPEC with the argument ARG in a call described by INFO
and update the overall result of the call in *RES. The format directive
@@ -1738,9 +1793,12 @@ format_directive (const pass_sprintf_length::call_
if (!fmtres.knownrange)
{
/* Only when the range is known, check it against the host value
- of INT_MAX. Otherwise the range doesn't correspond to known
+ of INT_MAX + 1. The plus one allows for the corner case of
+ width being specified by asterisk as INT_MIN. Larger values
+ are excessive and indication of some error expected to disable
+ the counter(s). Otherwise the range doesn't correspond to known
values of the argument. */
- if (fmtres.range.max >= target_int_max ())
+ if (fmtres.range.max > target_int_max () + 1)
{
/* Normalize the MAX counter to avoid having to deal with it
later. The counter can be less than HOST_WIDE_INT_M1U
@@ -1754,7 +1812,7 @@ format_directive (const pass_sprintf_length::call_
res->number_chars = HOST_WIDE_INT_M1U;
}
- if (fmtres.range.min >= target_int_max ())
+ if (fmtres.range.min > target_int_max () + 1)
{
/* Disable exact length checking after a failure to determine
even the minimum number of characters (it shouldn't happen
@@ -1794,7 +1852,7 @@ format_directive (const pass_sprintf_length::call_
(int)cvtlen, cvtbeg, fmtres.range.min,
navail);
}
- else if (fmtres.range.max < HOST_WIDE_INT_MAX)
+ else if (fmtres.range.max < target_int_max ())
{
const char* fmtstr
= (info.bounded
@@ -1866,6 +1924,13 @@ format_directive (const pass_sprintf_length::call_
if (warned && fmtres.argmin)
{
+ /* As an optimization when fmtres.argmin is a type, get
+ the range of values [TYPE_MIN, TYPE_MAX] from the type.
+ This makes it possible to avoid computing these for
+ every numerical argument of every directive even when
+ there is no diagnostic. */
+ maybe_get_value_range_from_type (&fmtres.argmin, &fmtres.argmax);
+
if (fmtres.argmin == fmtres.argmax)
inform (info.fmtloc, "directive argument %qE", fmtres.argmin);
else if (fmtres.bounded)
===================================================================
@@ -10,6 +10,7 @@
#endif
#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
char buffer [256];
extern char *ptr;
@@ -97,6 +98,9 @@ void test_sprintf_c_const (void)
T (-1, "%*cX", INT_MAX - 2, '1');
T (-1, "%*cX", INT_MAX - 1, '1');
T (-1, "%*cX", INT_MAX, '1'); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*c", INT_MIN + 1, '1');
+ T (-1, "%*c", INT_MIN, '1'); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
/* Verify that no warning is issued for calls that write into a flexible
@@ -263,6 +267,9 @@ void test_sprintf_chk_s_const (void)
T (-1, "%*s", INT_MAX, "");
T (-1, "X%*s", INT_MAX, ""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+ T (-1, "%-*s", INT_MIN + 1, "");
+ T (-1, "%-*s", INT_MIN, ""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
/* Multiple directives. */
T (1, "%s%s", "", "");
@@ -455,6 +462,14 @@ void test_sprintf_chk_hh_const (void)
T (5, "%0*hhd %0*hhi", 1, 12, 1, 123); /* { dg-warning ".%0\\*hhi. directive writing 3 bytes into a region of size 2" } */
T (5, "%0*hhd %0*hhi", 2, 12, 3, 123); /* { dg-warning ".%0\\*hhi. directive writing 3 bytes into a region of size 2" } */
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*hhi", INT_MAX - 1, 2);
+ T (-1, "%*hhi", INT_MAX, 3);
+ T (-1, "%*hhiX", INT_MAX, 4); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*hhi", INT_MIN + 1, 5);
+ T (-1, "%*hhi", INT_MIN, 6); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
/* FIXME: Move the boundary test cases into a file of their own that's
exercised only on targets with the matching type limits (otherwise
they'll fail). */
@@ -560,6 +575,14 @@ void test_sprintf_chk_h_const (void)
T (1, "%.0hX", 0);
T (1, "%#.0hX", 0);
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*hi", INT_MAX - 1, 2);
+ T (-1, "%*ho", INT_MAX, 3);
+ T (-1, "%*hoX", INT_MAX, 3); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*hu", INT_MIN + 1, 4);
+ T (-1, "%*hx", INT_MIN, 5); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
#undef MAX
#define MAX 65535
@@ -643,6 +666,14 @@ void test_sprintf_chk_integer_const (void)
T (1, "%.0X", 0);
T (1, "%#.0X", 0);
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*i", INT_MAX - 1, 2);
+ T (-1, "%*i", INT_MAX, 2);
+ T (-1, "%*oX", INT_MAX, 3); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*u", INT_MIN + 1, 4);
+ T (-1, "%*x", INT_MIN, 5); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
T ( 7, "%1$i%2$i%3$i", 1, 23, 456);
T ( 8, "%1$i%2$i%3$i%1$i", 1, 23, 456);
T ( 8, "%1$i%2$i%3$i%2$i", 1, 23, 456); /* { dg-warning "nul past the end" } */
@@ -964,6 +995,14 @@ void test_sprintf_chk_a_const (void)
T (7, "%.a", 0.0);
T (7, "%.0a", 0.0);
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*a", INT_MAX - 1, 2.0);
+ T (-1, "%*a", INT_MAX, 2.0);
+ T (-1, "%*aX", INT_MAX, 3.0); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*a", INT_MIN + 1, 4.0);
+ T (-1, "%*a", INT_MIN, 5.0); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
void test_sprintf_chk_e_const (void)
@@ -1021,6 +1060,14 @@ void test_sprintf_chk_e_const (void)
T (12, "%Le", 9.9999997e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999998e+99L);/* { dg-warning "terminating nul" } */
T (12, "%Le", 9.9999999e+99L);/* { dg-warning "terminating nul" } */
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*e", INT_MAX - 2, 1.0);
+ T (-1, "%*e", INT_MAX - 1, 2.0);
+ T (-1, "%*eX", INT_MAX, 3.0); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*e", INT_MIN + 1, 4.0);
+ T (-1, "%*e", INT_MIN, 5.0); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
/* At -Wformat-length level 1 unknown numbers are assumed to have
@@ -1200,6 +1247,14 @@ void test_sprintf_chk_hh_nonconst (int w, int p, i
T (2, "%*.1hhi", w, 123); /* { dg-warning "into a region" } */
T (2, "%*.2hhi", w, 123); /* { dg-warning "into a region" } */
T (2, "%*.3hhi", w, 123); /* { dg-warning "into a region" } */
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*hhi", INT_MAX - 1, a);
+ T (-1, "%*hhi", INT_MAX, a);
+ T (-1, "%*hhiX", INT_MAX, a); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*hhi", INT_MIN + 1, a);
+ T (-1, "%*hhi", INT_MIN, a); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
/* Exercise the h length modifier with all integer specifiers and
@@ -1246,6 +1301,14 @@ void test_sprintf_chk_h_nonconst (int a)
T (3, "%2ho", a);
T (3, "%2hu", a);
T (3, "%2hx", a);
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*hd", INT_MAX - 1, a);
+ T (-1, "%*hi", INT_MAX, a);
+ T (-1, "%*hiX", INT_MAX, a); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*hi", INT_MIN + 1, a);
+ T (-1, "%*hi", INT_MIN, a); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
/* Exercise all integer specifiers with no modifier and a non-constant
@@ -1294,6 +1357,14 @@ void test_sprintf_chk_int_nonconst (int w, int p,
T (3, "%2x", a);
T (1, "%.*d", p, a);
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*d", INT_MAX - 1, a);
+ T (-1, "%*i", INT_MAX, a);
+ T (-1, "%*oX", INT_MAX, a); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*u", INT_MIN + 1, a);
+ T (-1, "%*x", INT_MIN, a); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
void test_sprintf_chk_e_nonconst (int w, int p, double d)
@@ -1336,14 +1407,31 @@ void test_sprintf_chk_e_nonconst (int w, int p, do
T ( 8, "%.1e", d);
T ( 0, "%*e", 0, d); /* { dg-warning "writing between 12 and 14 bytes into a region of size 0" } */
+
+ /* The number of bytes written by the directive below is actually between
+ 12 and INT_MAX + 1 but mentioning the upper bound would be confusing
+ because it could only happen if w were INT_MIN, which is both highly
+ unlikely and undefined. */
T ( 0, "%*e", w, d); /* { dg-warning "writing 12 or more bytes into a region of size 0" } */
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*e", INT_MAX - 1, d);
+ T (-1, "%*e", INT_MAX, d);
+ T (-1, "%*eX", INT_MAX, d); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*e", INT_MIN + 1, d);
+ T (-1, "%*e", INT_MIN, d); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
-void test_sprintf_chk_f_nonconst (double d)
+void test_sprintf_chk_f_nonconst (int w, int p, double d)
{
- T (-1, "%F", d);
- T (-1, "%lF", d);
+ T (-1, "%F", d);
+ T (-1, "%lF", d);
+ T (-1, "%*f", w, d);
+ T (-1, "%.*f", p, d);
+ T (-1, "%*.*f", w, p, d);
+
T ( 0, "%F", d); /* { dg-warning "into a region" } */
T ( 0, "%f", d); /* { dg-warning "into a region" } */
T ( 1, "%F", d); /* { dg-warning "into a region" } */
@@ -1360,10 +1448,29 @@ void test_sprintf_chk_e_nonconst (int w, int p, do
T ( 6, "%f", d); /* { dg-warning "into a region" } */
T ( 7, "%F", d); /* { dg-warning "into a region" } */
T ( 7, "%f", d); /* { dg-warning "into a region" } */
+ T ( 7, "%*f", w, d); /* { dg-warning "into a region" } */
+
+ /* The unknown precision below could reduce the number of bytes to less
+ than 7 so no warning is expected. */
+ T ( 7, "%.*f", p, d);
+ T ( 7, "%*.*f", w, p, d);
+
T ( 8, "%F", d); /* { dg-warning "nul past the end" } */
T ( 8, "%f", d); /* { dg-warning "nul past the end" } */
T ( 9, "%F", d);
T ( 9, "%f", d);
+
+ T (-1, "%*f", w, d);
+ T (-1, "%.*f", p, d);
+ T (-1, "%*.*f", w, p, d);
+
+ /* Verify the edge case of excessive width specified by asterisk. */
+ T (-1, "%*f", INT_MAX - 1, d);
+ T (-1, "%*f", INT_MAX, d);
+ T (-1, "%*fX", INT_MAX, d); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+ T (-1, "%*f", INT_MIN + 1, d);
+ T (-1, "%*f", INT_MIN, d); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
}
/* Tests for __builtin_vsprintf_chk are the same as those for