PR middle-end/78703 - fprintf-return-value floating point handling incorrect in locales with a mulltibyte decimal point

gcc/ChangeLog:
        PR middle-end/78703
        * gimple-ssa-sprintf.c (adjust_for_width_or_precision): Change
        to accept adjustment as an array.
        (get_int_range): New function.
        (struct directive): Make width and prec arrays.
        (directive::set_width, directive::set_precision): Call get_int_range.
        (format_integer, format_floating): Handle width and precision ranges.
        (format_string, parse_directive): Same.

gcc/testsuite/ChangeLog:
        PR middle-end/78703
        * gcc.dg/tree-ssa/builtin-snprintf-warn-1.c: Update
        * gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: Rename...
        * gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: ...to this.
        * gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.

From-SVN: r244956
This commit is contained in:
Martin Sebor 2017-01-26 23:07:02 +00:00 committed by Martin Sebor
parent b0670cc0eb
commit 31c87a433c
6 changed files with 697 additions and 445 deletions

View File

@ -1,3 +1,14 @@
2017-01-26 Martin Sebor <msebor@redhat.com>
PR middle-end/78703
* gimple-ssa-sprintf.c (adjust_for_width_or_precision): Change
to accept adjustment as an array.
(get_int_range): New function.
(struct directive): Make width and prec arrays.
(directive::set_width, directive::set_precision): Call get_int_range.
(format_integer, format_floating): Handle width and precision ranges.
(format_string, parse_directive): Same.
2017-01-26 Jakub Jelinek <jakub@redhat.com>
PR debug/79129

View File

@ -478,15 +478,15 @@ struct fmtresult
range.unlikely = max;
}
/* Adjust result upward to reflect the value the specified width
or precision is known to have. */
fmtresult& adjust_for_width_or_precision (HOST_WIDE_INT,
/* Adjust result upward to reflect the RANGE of values the specified
width or precision is known to be in. */
fmtresult& adjust_for_width_or_precision (const HOST_WIDE_INT[2],
tree = NULL_TREE,
unsigned = 0, unsigned = 0);
/* Return the maximum number of decimal digits a value of TYPE
formats as on output. */
static unsigned type_max_digits (tree type, int base);
static unsigned type_max_digits (tree, int);
/* The range a directive's argument is in. */
tree argmin, argmax;
@ -504,32 +504,21 @@ struct fmtresult
bool nullp;
};
/* Adjust result upward to reflect the value SCALAR_ADJUST the specified
width or precision is known to have. When non-null, TYPE denotes the
type of the directive whose result is being adjusted, BASE gives the
base of the directive (octal, decimal, or hex), and ADJ denotes
the additional adjustment to the LIKELY counter that may need to be
added when SCALAR_ADJUST represents a range. */
/* Adjust result upward to reflect the range ADJUST of values the
specified width or precision is known to be in. When non-null,
TYPE denotes the type of the directive whose result is being
adjusted, BASE gives the base of the directive (octal, decimal,
or hex), and ADJ denotes the additional adjustment to the LIKELY
counter that may need to be added when ADJUST is a range. */
fmtresult&
fmtresult::adjust_for_width_or_precision (HOST_WIDE_INT scalar_adjust,
fmtresult::adjust_for_width_or_precision (const HOST_WIDE_INT adjust[2],
tree type /* = NULL_TREE */,
unsigned base /* = 0 */,
unsigned adj /* = 0 */)
{
bool minadjusted = false;
/* Translate SCALAR_ADJUST to a "fake" range until width and precision
ranges are handled. */
HOST_WIDE_INT adjust[2];
if (scalar_adjust == HOST_WIDE_INT_MIN)
{
adjust[0] = -1;
adjust[1] = target_int_max () + 1;
}
else
adjust[0] = adjust[1] = scalar_adjust;
/* Adjust the minimum and likely counters. */
if (0 <= adjust[0])
{
@ -609,7 +598,12 @@ fmtresult::type_max_digits (tree type, int base)
return prec * 301 / 1000 + 1;
}
/* Description of a format directive. */
static bool
get_int_range (tree, tree, HOST_WIDE_INT *, HOST_WIDE_INT *,
bool, HOST_WIDE_INT);
/* Description of a format directive. A directive is either a plain
string or a conversion specification that starts with '%'. */
struct directive
{
@ -623,10 +617,11 @@ struct directive
/* A bitmap of flags, one for each character. */
unsigned flags[256 / sizeof (int)];
/* The specified width, or -1 if not specified. */
HOST_WIDE_INT width;
/* The specified precision, or -1 if not specified. */
HOST_WIDE_INT prec;
/* The range of values of the specified width, or -1 if not specified. */
HOST_WIDE_INT width[2];
/* The range of values of the specified precision, or -1 if not
specified. */
HOST_WIDE_INT prec[2];
/* Length modifier. */
format_lengths modifier;
@ -666,54 +661,36 @@ struct directive
&= ~(1U << (c % (CHAR_BIT * sizeof *flags)));
}
/* Set the width to VAL. */
/* Set both bounds of the width range to VAL. */
void set_width (HOST_WIDE_INT val)
{
width = val;
width[0] = width[1] = val;
}
/* Set the width to ARG. */
/* Set the width range according to ARG, with both bounds being
no less than 0. For a constant ARG set both bounds to its value
or 0, whichever is greater. For a non-constant ARG in some range
set width to its range adjusting each bound to -1 if it's less.
For an indeterminate ARG set width to [0, INT_MAX]. */
void set_width (tree arg)
{
if (tree_fits_shwi_p (arg))
{
width = tree_to_shwi (arg);
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;
get_int_range (arg, integer_type_node, width, width + 1, true, 0);
}
/* Set the precision to val. */
/* Set both bounds of the precision range to VAL. */
void set_precision (HOST_WIDE_INT val)
{
prec = val;
prec[0] = prec[1] = val;
}
/* Set the precision to ARG. */
/* Set the precision range according to ARG, with both bounds being
no less than -1. For a constant ARG set both bounds to its value
or -1 whichever is greater. For a non-constant ARG in some range
set precision to its range adjusting each bound to -1 if it's less.
For an indeterminate ARG set precision to [-1, INT_MAX]. */
void set_precision (tree arg)
{
if (tree_fits_shwi_p (arg))
{
prec = tree_to_shwi (arg);
if (prec < 0)
prec = -1;
}
else
prec = HOST_WIDE_INT_MIN;
get_int_range (arg, integer_type_node, prec, prec + 1, false, -1);
}
};
@ -797,7 +774,7 @@ tree_digits (tree x, int base, HOST_WIDE_INT prec, bool plus, bool prefix)
}
/* Given the formatting result described by RES and NAVAIL, the number
of available in the destination, return the number of bytes remaining
of available in the destination, return the range of bytes remaining
in the destination. */
static inline result_range
@ -816,9 +793,6 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res)
minus the minimum. */
range.max = res.range.min < navail ? navail - res.range.min : 0;
/* Given the formatting result described by RES and NAVAIL, the number
of available in the destination, return the minimum number of bytes
remaining in the destination. */
range.likely = res.range.likely < navail ? navail - res.range.likely : 0;
if (res.range.max < HOST_WIDE_INT_MAX)
@ -941,6 +915,96 @@ build_intmax_type_nodes (tree *pintmax, tree *puintmax)
}
}
/* Determine the range [*PMIN, *PMAX] that the expression ARG of TYPE
is in. Return true when the range is a subrange of that of TYPE.
Whn ARG is null it is as if it had the full range of TYPE.
When ABSOLUTE is true the range reflects the absolute value of
the argument. When ABSOLUTE is false, negative bounds of
the determined range are replaced with NEGBOUND. */
static bool
get_int_range (tree arg, tree type, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
bool absolute, HOST_WIDE_INT negbound)
{
bool knownrange = false;
if (!arg)
{
*pmin = (TYPE_UNSIGNED (type)
? tree_to_uhwi (TYPE_MIN_VALUE (type))
: tree_to_shwi (TYPE_MIN_VALUE (type)));
*pmax = tree_to_uhwi (TYPE_MAX_VALUE (type));
}
else if (TREE_CODE (arg) == INTEGER_CST)
{
/* For a constant argument return its value adjusted as specified
by NEGATIVE and NEGBOUND and return true to indicate that the
result is known. */
*pmin = tree_fits_shwi_p (arg) ? tree_to_shwi (arg) : tree_to_uhwi (arg);
*pmax = *pmin;
knownrange = true;
}
else
{
/* True if the argument's range cannot be determined. */
bool unknown = true;
type = TREE_TYPE (arg);
if (TREE_CODE (arg) == SSA_NAME
&& TREE_CODE (type) == INTEGER_TYPE)
{
/* Try to determine the range of values of the integer argument. */
wide_int min, max;
enum value_range_type range_type = get_range_info (arg, &min, &max);
if (range_type == VR_RANGE)
{
HOST_WIDE_INT type_min
= (TYPE_UNSIGNED (type)
? tree_to_uhwi (TYPE_MIN_VALUE (type))
: tree_to_shwi (TYPE_MIN_VALUE (type)));
HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (type));
*pmin = min.to_shwi ();
*pmax = max.to_shwi ();
/* Return true if the adjusted range is a subrange of
the full range of the argument's type. */
knownrange = type_min < *pmin || *pmax < type_max;
unknown = false;
}
}
/* Handle an argument with an unknown range as if none had been
provided. */
if (unknown)
return get_int_range (NULL_TREE, type, pmin, pmax, absolute, negbound);
}
/* Adjust each bound as specified by ABSOLUTE and NEGBOUND. */
if (absolute)
{
if (*pmin < 0)
{
if (*pmin == *pmax)
*pmin = *pmax = -*pmin;
else
{
HOST_WIDE_INT tmp = -*pmin;
*pmin = 0;
if (*pmax < tmp)
*pmax = tmp;
}
}
}
else if (*pmin < negbound)
*pmin = negbound;
return knownrange;
}
/* With the range [*ARGMIN, *ARGMAX] of an integer directive's actual
argument, due to the conversion from either *ARGMIN or *ARGMAX to
the type of the directive's formal argument it's possible for both
@ -1005,8 +1069,9 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
}
/* Return a range representing the minimum and maximum number of bytes
that the directive DIR will write on output for the integer argument
ARG when non-null. ARG may be null (for vararg functions). */
that the format directive DIR will output for any argument given
the WIDTH and PRECISION (extracted from DIR). This function is
used when the directive argument or its value isn't known. */
static fmtresult
format_integer (const directive &dir, tree arg)
@ -1118,11 +1183,9 @@ format_integer (const directive &dir, tree arg)
{
/* When a constant argument has been provided use its value
rather than type to determine the length of the output. */
fmtresult res;
if ((dir.prec == HOST_WIDE_INT_MIN || dir.prec == 0)
&& integer_zerop (arg))
if ((dir.prec[0] <= 0 && dir.prec[1] >= 0) && integer_zerop (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
@ -1132,7 +1195,7 @@ format_integer (const directive &dir, tree arg)
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 == HOST_WIDE_INT_MIN)
if (res.range.min == 0 && dir.prec[0] != dir.prec[1])
{
res.range.max = 1;
res.range.likely = 1;
@ -1148,17 +1211,13 @@ format_integer (const directive &dir, tree arg)
/* Convert the argument to the type of the directive. */
arg = fold_convert (dirtype, arg);
res.range.min = tree_digits (arg, base, dir.prec,
res.range.min = tree_digits (arg, base, dir.prec[0],
maybesign, maybebase);
/* Set the maximum to INT_MAX when precision is specified
but unknown (because it can be as large as that) otherwise
to the minimum and have it adjusted below. */
if (dir.prec == HOST_WIDE_INT_MIN)
res.range.max = target_int_max ();
else
if (dir.prec[0] == dir.prec[1])
res.range.max = res.range.min;
else
res.range.max = tree_digits (arg, base, dir.prec[1],
maybesign, maybebase);
res.range.likely = res.range.min;
}
@ -1170,6 +1229,7 @@ format_integer (const directive &dir, tree arg)
/* Bump up the counters again if PRECision is greater still. */
res.adjust_for_width_or_precision (dir.prec, dirtype, base,
(sign | maybebase) + (base == 16));
return res;
}
else if (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE
@ -1254,7 +1314,7 @@ format_integer (const directive &dir, tree arg)
can output. When precision may be zero, use zero as the minimum
since it results in no bytes on output (unless width is specified
to be greater than 0). */
bool zero = dir.prec == 0 || dir.prec == HOST_WIDE_INT_MIN;
bool zero = dir.prec[0] <= 0 && dir.prec[1] >= 0;
argmin = build_int_cst (argtype, !zero);
int typeprec = TYPE_PRECISION (dirtype);
@ -1438,16 +1498,15 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
round-to-nearest mode. */
mpfr_t x;
mpfr_init2 (x, rfmt->p);
mpfr_from_real (x, &rv, GMP_RNDN);
mpfr_from_real (x, &rv, MPFR_RNDN);
/* Return a value one greater to account for the leading minus sign. */
return 1 + get_mpfr_format_length (x, "", prec, spec, 'D');
}
/* Return a range representing the minimum and maximum number of bytes
that the format directive DIR will output for any argument given
the WIDTH and PRECISION (extracted from DIR). This function is
used when the directive argument or its value isn't known. */
that the directive DIR will output for any argument. This function
is used when the directive argument or its value isn't known. */
static fmtresult
format_floating (const directive &dir)
@ -1493,10 +1552,10 @@ format_floating (const directive &dir)
case 'a':
{
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
if (dir.prec <= 0)
if (dir.prec[0] <= 0)
minprec = 0;
else if (dir.prec > 0)
minprec = dir.prec + !radix /* decimal point */;
else if (dir.prec[0] > 0)
minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = (2 /* 0x */
+ flagmin
@ -1504,17 +1563,15 @@ format_floating (const directive &dir)
+ minprec
+ 3 /* p+0 */);
HOST_WIDE_INT maxprec
= dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
res.range.max = format_floating_max (type, 'a', maxprec);
res.range.max = format_floating_max (type, 'a', dir.prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
if (dir.prec != 0)
res.range.unlikely = res.range.max + target_mb_len_max () - 1;
else
res.range.unlikely = res.range.max;
res.range.unlikely = res.range.max;
if (dir.prec[0] != dir.prec[1]
|| dir.prec[0] == -1 || dir.prec[0] > 0)
res.range.unlikely += target_mb_len_max () - 1;
break;
}
@ -1525,28 +1582,26 @@ format_floating (const directive &dir)
/* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
if (dir.prec == HOST_WIDE_INT_MIN || dir.prec == 0)
if ((dir.prec[0] < 0 && dir.prec[1] > -1) || dir.prec[0] == 0)
minprec = 0;
else if (dir.prec > 0)
minprec = dir.prec + !radix /* decimal point */;
else if (dir.prec[0] > 0)
minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = (flagmin
+ radix
+ minprec
+ 2 /* e+ */ + 2);
/* MPFR uses a precision of 16 by default for some reason.
Set it to the C default of 6. */
HOST_WIDE_INT maxprec
= (dir.prec == HOST_WIDE_INT_MIN ? target_int_max ()
: dir.prec < 0 ? 6 : dir.prec);
int maxprec = dir.prec[1] < 0 ? 6 : dir.prec[1];
res.range.max = format_floating_max (type, 'e', maxprec);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
if (dir.prec != 0)
if (dir.prec[0] != dir.prec[1]
|| dir.prec[0] == -1 || dir.prec[0] > 0)
res.range.unlikely = res.range.max + target_mb_len_max () -1;
else
res.range.unlikely = res.range.max;
@ -1562,25 +1617,22 @@ format_floating (const directive &dir)
when precision is greater than zero, then the lower bound
is 2 plus precision (plus flags). */
HOST_WIDE_INT minprec = 0;
if (dir.prec == HOST_WIDE_INT_MIN)
minprec = 0;
else if (dir.prec < 0)
minprec = 6 + !radix /* decimal point */;
else if (dir.prec)
minprec = dir.prec + !radix /* decimal point */;
if (dir.prec[0] < 0)
minprec = dir.prec[1] < 0 ? 6 + !radix /* decimal point */ : 0;
else if (dir.prec[0])
minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = flagmin + radix + minprec;
/* Compute the upper bound for -TYPE_MAX. */
HOST_WIDE_INT maxprec
= dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
res.range.max = format_floating_max (type, 'f', maxprec);
res.range.max = format_floating_max (type, 'f', dir.prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
if (dir.prec != 0)
if (dir.prec[0] != dir.prec[1]
|| dir.prec[0] == -1 || dir.prec[0] > 0)
res.range.unlikely = res.range.max + target_mb_len_max () - 1;
break;
}
@ -1593,10 +1645,7 @@ format_floating (const directive &dir)
the lower bound on the range of bytes (not counting flags
or width) is 1. */
res.range.min = flagmin;
HOST_WIDE_INT maxprec
= dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
res.range.max = format_floating_max (type, 'g', maxprec);
res.range.max = format_floating_max (type, 'g', dir.prec[1]);
res.range.likely = res.range.max;
/* The unlikely maximum accounts for the longest multibyte
@ -1611,13 +1660,12 @@ format_floating (const directive &dir)
/* Bump up the byte counters if WIDTH is greater. */
res.adjust_for_width_or_precision (dir.width);
return res;
}
/* Return a range representing the minimum and maximum number of bytes
that the conversion specification DIR will write on output for the
floating argument ARG. */
that the directive DIR will write on output for the floating argument
ARG. */
static fmtresult
format_floating (const directive &dir, tree arg)
@ -1625,7 +1673,7 @@ format_floating (const directive &dir, tree arg)
if (!arg || TREE_CODE (arg) != REAL_CST)
return format_floating (dir);
HOST_WIDE_INT prec[] = { dir.prec, dir.prec };
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
if (TOUPPER (dir.specifier) == 'A')
{
@ -1724,7 +1772,6 @@ format_floating (const directive &dir, tree arg)
}
res.adjust_for_width_or_precision (dir.width);
return res;
}
@ -1815,35 +1862,40 @@ format_character (const directive &dir, tree arg)
if (dir.modifier == FMT_LEN_l)
{
unsigned HOST_WIDE_INT val;
if (arg && TREE_CODE (arg) == INTEGER_CST && tree_fits_shwi_p (arg))
val = tree_to_shwi (arg);
else
val = HOST_WIDE_INT_MAX;
/* A wide character can result in as few as zero bytes. */
res.range.min = 0;
if (val == 0)
HOST_WIDE_INT min, max;
if (get_int_range (arg, integer_type_node, &min, &max, false, 0))
{
/* The NUL wide character results in no bytes. */
res.range.max = 0;
res.range.likely = 0;
res.range.unlikely = 0;
}
else if (0 < val && val < 128)
{
/* A wide character in the ASCII range most likely results
in a single byte, and only unlikely in up to MB_LEN_MAX. */
res.range.max = 1;
res.range.likely = 1;
res.range.unlikely = target_mb_len_max ();
if (min == 0 && max == 0)
{
/* The NUL wide character results in no bytes. */
res.range.max = 0;
res.range.likely = 0;
res.range.unlikely = 0;
}
else if (0 < min && min < 128)
{
/* A wide character in the ASCII range most likely results
in a single byte, and only unlikely in up to MB_LEN_MAX. */
res.range.max = 1;
res.range.likely = 1;
res.range.unlikely = target_mb_len_max ();
}
else
{
/* A wide character outside the ASCII range likely results
in up to two bytes, and only unlikely in up to MB_LEN_MAX. */
res.range.max = target_mb_len_max ();
res.range.likely = 2;
res.range.unlikely = res.range.max;
}
}
else
{
/* A wide character outside the ASCII range likely results
in up to two bytes, and only unlikely in up to MB_LEN_MAX. */
/* An unknown wide character is treated the same as a wide
character outside the ASCII range. */
res.range.max = target_mb_len_max ();
res.range.likely = 2;
res.range.unlikely = res.range.max;
@ -1853,7 +1905,7 @@ format_character (const directive &dir, tree arg)
{
/* A plain '%c' directive. Its ouput is exactly 1. */
res.range.min = res.range.max = 1;
res.range.likely = res.range.unlikely = res.range.min;
res.range.likely = res.range.unlikely = 1;
res.knownrange = true;
}
@ -1862,9 +1914,9 @@ format_character (const directive &dir, tree arg)
}
/* Return the minimum and maximum number of characters formatted
by the '%c' and '%s' format directives and ther wide character
forms for the argument ARG. ARG can be null (for functions
such as vsprinf). */
by the '%s' format directive and its wide character form for
the argument ARG. ARG can be null (for functions such as
vsprinf). */
static fmtresult
format_string (const directive &dir, tree arg)
@ -1892,18 +1944,19 @@ format_string (const directive &dir, tree arg)
2 * wcslen (S).*/
res.range.likely = res.range.min * 2;
/* For a wide character string, use precision as the maximum
even if precision is greater than the string length since
the number of bytes the string converts to may be greater
(due to MB_CUR_MAX). */
if (0 <= dir.prec
&& (unsigned HOST_WIDE_INT)dir.prec < res.range.max)
if (0 <= dir.prec[1]
&& (unsigned HOST_WIDE_INT)dir.prec[1] < res.range.max)
{
res.range.max = dir.prec;
res.range.likely = dir.prec;
res.range.unlikely = dir.prec;
res.range.max = dir.prec[1];
res.range.likely = dir.prec[1];
res.range.unlikely = dir.prec[1];
}
if (dir.prec[0] < 0 && dir.prec[1] > -1)
res.range.min = 0;
else if (0 <= dir.prec[0])
res.range.likely = dir.prec[0];
/* Even a non-empty wide character string need not convert into
any bytes. */
res.range.min = 0;
@ -1912,20 +1965,16 @@ format_string (const directive &dir, tree arg)
{
res.knownrange = true;
if (dir.prec == HOST_WIDE_INT_MIN)
if (dir.prec[0] < 0 && dir.prec[1] > -1)
res.range.min = 0;
else if ((unsigned HOST_WIDE_INT)dir.prec < res.range.min)
else if ((unsigned HOST_WIDE_INT)dir.prec[0] < res.range.min)
res.range.min = dir.prec[0];
if ((unsigned HOST_WIDE_INT)dir.prec[1] < res.range.max)
{
res.range.min = dir.prec;
res.range.max = dir.prec;
res.range.likely = dir.prec;
res.range.unlikely = dir.prec;
}
else if ((unsigned HOST_WIDE_INT)dir.prec < res.range.max)
{
res.range.max = dir.prec;
res.range.likely = dir.prec;
res.range.unlikely = dir.prec;
res.range.max = dir.prec[1];
res.range.likely = dir.prec[1];
res.range.unlikely = dir.prec[1];
}
}
}
@ -1945,20 +1994,20 @@ format_string (const directive &dir, tree arg)
in mode 2, and the maximum is PRECISION or -1 to disable
tracking. */
if (0 <= dir.prec)
if (0 <= dir.prec[0])
{
if (slen.range.min >= target_int_max ())
slen.range.min = 0;
else if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.min)
else if ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.min)
{
slen.range.min = dir.prec;
slen.range.min = dir.prec[0];
slen.range.likely = slen.range.min;
}
if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.max
if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max
|| slen.range.max >= target_int_max ())
{
slen.range.max = dir.prec;
slen.range.max = dir.prec[1];
slen.range.likely = slen.range.max;
}
}
@ -2357,35 +2406,32 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
avail_range.min, avail_range.max);
}
/* Compute the length of the output resulting from the conversion
specification DIR with the argument ARG in a call described by INFO
and update the overall result of the call in *RES. The format directive
corresponding to DIR starts at CVTBEG and is CVTLEN characters long. */
/* Compute the length of the output resulting from the directive DIR
in a call described by INFO and update the overall result of the call
in *RES. Return true if the directive has been handled. */
static bool
format_directive (const pass_sprintf_length::call_info &info,
format_result *res, const directive &dir)
{
const char *cvtbeg = dir.beg;
size_t cvtlen = dir.len;
tree arg = dir.arg;
/* Offset of the beginning of the directive from the beginning
of the format string. */
size_t offset = cvtbeg - info.fmtstr;
size_t offset = dir.beg - info.fmtstr;
size_t start = offset;
size_t length = offset + dir.len - !!dir.len;
/* Create a location for the whole directive from the % to the format
specifier. */
substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
offset, offset, offset + cvtlen - 1);
offset, start, length);
/* Also create a location range for the argument if possible.
This doesn't work for integer literals or function calls. */
source_range argrange;
source_range *pargrange;
if (arg && CAN_HAVE_LOCATION_P (arg))
if (dir.arg && CAN_HAVE_LOCATION_P (dir.arg))
{
argrange = EXPR_LOCATION_RANGE (arg);
argrange = EXPR_LOCATION_RANGE (dir.arg);
pargrange = &argrange;
}
else
@ -2396,8 +2442,8 @@ format_directive (const pass_sprintf_length::call_info &info,
if (!dir.fmtfunc || res->range.min >= HOST_WIDE_INT_MAX)
return false;
/* Compute the (approximate) length of the formatted output. */
fmtresult fmtres = dir.fmtfunc (dir, arg);
/* Compute the range of lengths of the formatted output. */
fmtresult fmtres = dir.fmtfunc (dir, dir.arg);
/* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are
@ -2525,7 +2571,6 @@ format_directive (const pass_sprintf_length::call_info &info,
}
}
/* Has the minimum directive output length exceeded INT_MAX? */
/* Has the likely and maximum directive output exceeded INT_MAX? */
bool likelyximax = *dir.beg && res->range.likely > target_int_max ();
bool maxximax = *dir.beg && res->range.max > target_int_max ();
@ -2949,7 +2994,8 @@ parse_directive (pass_sprintf_length::call_info &info,
{
/* Width specified by a va_list takes on the range [0, -INT_MIN]
(width is the absolute value of that specified). */
dir.width = HOST_WIDE_INT_MIN;
dir.width[0] = 0;
dir.width[1] = target_int_max () + 1;
}
}
else
@ -2963,7 +3009,8 @@ parse_directive (pass_sprintf_length::call_info &info,
{
/* Precision specified by a va_list takes on the range [-1, INT_MAX]
(unlike width, negative precision is ignored). */
dir.prec = HOST_WIDE_INT_MIN;
dir.prec[0] = -1;
dir.prec[1] = target_int_max ();
}
}
else
@ -2986,11 +3033,22 @@ parse_directive (pass_sprintf_length::call_info &info,
dir.dirno, (size_t)(dir.beg - info.fmtstr),
(int)dir.len, dir.beg);
if (star_width)
fprintf (dump_file, ", width = %lli", (long long)dir.width);
{
if (dir.width[0] == dir.width[1])
fprintf (dump_file, ", width = %lli", (long long)dir.width[0]);
else
fprintf (dump_file, ", width in range [%lli, %lli]",
(long long)dir.width[0], (long long)dir.width[1]);
}
if (star_precision)
fprintf (dump_file, ", precision = %lli", (long long)dir.prec);
{
if (dir.prec[0] == dir.prec[1])
fprintf (dump_file, ", precision = %lli", (long long)dir.prec[0]);
else
fprintf (dump_file, ", precision in range [%lli, %lli]",
(long long)dir.prec[0], (long long)dir.prec[1]);
}
fputc ('\n', dump_file);
}
@ -3019,7 +3077,7 @@ pass_sprintf_length::compute_format_length (call_info &info,
(unsigned long long)info.objsize, info.fmtstr);
}
/* Reset the minimum and maximum bytes counters. */
/* Reset the minimum and maximum byte counters. */
res->range.min = res->range.max = 0;
/* No directive has been seen yet so the length of output is bounded

View File

@ -1,3 +1,11 @@
2017-01-26 Martin Sebor <msebor@redhat.com>
PR middle-end/78703
* gcc.dg/tree-ssa/builtin-snprintf-warn-1.c: Update
* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: Rename...
* gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: ...to this.
* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.
2017-01-26 Jakub Jelinek <jakub@redhat.com>
PR debug/79129

View File

@ -4,6 +4,9 @@
typedef struct
{
char a0[0];
/* Separate a0 from a1 to prevent the former from being substituted
for the latter and causing false positives. */
int: 8;
char a1[1];
char a2[2];
char a3[3];
@ -23,11 +26,13 @@ int value_range (int min, int max)
#define R(min, max) value_range (min, max)
extern void sink (void*);
/* Verify that calls to snprintf whose return value is unused are
diagnosed if certain or possible truncation is detected. */
#define T(size, ...) \
__builtin_snprintf (buffer (size), size, __VA_ARGS__)
__builtin_snprintf (buffer (size), size, __VA_ARGS__), sink (buffer)
void test_int_retval_unused (void)
{
@ -39,9 +44,20 @@ void test_int_retval_unused (void)
void test_string_retval_unused (const Arrays *ar)
{
/* At level 1 strings of unknown length are assumed to be empty so
the following is not diagnosed. */
T (1, "%-s", ar->a0);
/* A one-byte array can only hold an empty string, so the following
isn't diagnosed. */
T (1, "%-s", ar->a1);
T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */
/* Unlike the ar->a0 case above, at level 1, the length of an unknown
string that points to an array of known size is assumed to be the
size of the array minus 1. */
T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */
T (1, "%-s", ar->a3); /* { dg-warning "output may be truncated" } */
T (1, "%-s", ar->a4); /* { dg-warning "output may be truncated" } */
/* Same as the ar->a0 case above. */
T (1, "%-s", ar->ax);
}
@ -68,6 +84,7 @@ void test_string_retval_used (const Arrays *ar)
T (1, "%-s", ar->a0);
T (1, "%-s", ar->a1);
T (1, "%-s", ar->a2);
T (1, "%-s", ar->a3);
T (1, "%-s", ar->a4);
T (1, "%-s", "123"); /* { dg-warning "output truncated" } */
}

View File

@ -0,0 +1,270 @@
/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10
Test to verify the correctness of ranges of output computed for floating
point directives.
{ dg-do compile }
{ dg-options "-O2 -Wformat -Wformat-overflow -ftrack-macro-expansion=0" } */
typedef __builtin_va_list va_list;
char dst[1];
extern void sink (int, void*);
/* Macro to test either width or precision specified by the asterisk
(but not both). */
#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst)
/* Macro to test both width and precision specified by the asterisk. */
#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst)
/* Macro to test vsprintf with both width and precision specified by
the asterisk. */
#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst)
/* Exercise %a. */
void test_a (int w, int p, double x)
{
T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */
T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */
T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */
T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */
T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */
T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
T1 ("%*.a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T1 ("%*.0a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T1 ("%*.1a", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T1 ("%*.2a", w); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
T1 ("%.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%1.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%2.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%3.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
}
/* Exercise %e. */
void test_e (int w, int p, double x)
{
T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */
T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */
T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */
T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */
T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */
T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
T1 ("%*.e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
T1 ("%*.0e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
T1 ("%*.1e", w); /* { dg-warning "writing between 7 and 2147483648 bytes" } */
T1 ("%*.2e", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T1 ("%.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%1.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%2.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%3.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
}
/* Exercise %f. */
void test_f (int w, int p, double x)
{
T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */
T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */
T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */
T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */
T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */
T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */
T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */
T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */
T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
T1 ("%*.f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
T1 ("%*.0f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
T1 ("%*.1f", w); /* { dg-warning "writing between 3 and 2147483648 bytes" } */
T1 ("%*.2f", w); /* { dg-warning "writing between 4 and 2147483648 bytes" } */
T1 ("%.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T1 ("%1.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T1 ("%2.*f", p); /* { dg-warning "writing between 2 and 2147483958 bytes" } */
T1 ("%3.*f", p); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
void test_g (double x)
{
T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */
T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */
T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */
T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */
T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */
T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */
T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */
}
/* Exercise %a. */
void test_a_va (va_list va)
{
T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */
T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */
T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */
T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */
T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */
T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
T ("%*.a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T ("%*.0a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T ("%*.1a"); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T ("%*.2a"); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
T ("%.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%1.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%2.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%6.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%9.*a"); /* { dg-warning "writing between 9 and 2147483658 bytes" } */
T ("%*.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
}
/* Exercise %e. */
void test_e_va (va_list va)
{
T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */
T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */
T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */
T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */
T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */
T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
T ("%.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T ("%1.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T ("%6.*e"); /* { dg-warning "writing between 6 and 2147483655 bytes" } */
T ("%9.*e"); /* { dg-warning "writing between 9 and 2147483655 bytes" } */
T ("%*.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
}
/* Exercise %f. */
void test_f_va (va_list va)
{
T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */
T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */
T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */
T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */
T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */
T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */
T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */
T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
T ("%.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T ("%1.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T ("%3.*f"); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
T ("%*.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
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" } */
T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */
T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */
T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */
T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */
T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */
T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */
T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */
T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
T ("%.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
T ("%1.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
T ("%4.*g"); /* { dg-warning "writing between 4 and 310 bytes" } */
T ("%*.*g"); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
}

View File

@ -1,270 +1,158 @@
/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10
Test to verify the correctness of ranges of output computed for floating
point directives.
{ dg-do compile }
{ dg-options "-O2 -Wformat -Wformat-overflow -ftrack-macro-expansion=0" } */
/* { dg-do compile } */
/* { dg-options "-O2 -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */
typedef __builtin_va_list va_list;
typedef __SIZE_TYPE__ size_t;
char dst[1];
#define INT_MAX __INT_MAX__
#define INT_MIN (-INT_MAX - 1)
extern void sink (int, void*);
#ifndef LINE
# define LINE 0
#endif
/* Macro to test either width or precision specified by the asterisk
(but not both). */
#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst)
int dummy_sprintf (char*, const char*, ...);
void sink (void*);
/* Macro to test both width and precision specified by the asterisk. */
#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst)
char buffer[4096];
char *ptr;
/* Macro to test vsprintf with both width and precision specified by
the asterisk. */
#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst)
/* 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)
/* Exercise %a. */
void test_a (int w, int p, double x)
/* 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)
#define T(bufsize, fmt, ...) \
do { \
char *buf = buffer (bufsize); \
FUNC (sprintf)(buf, fmt, __VA_ARGS__); \
sink (buf); \
} while (0)
/* Identity function to verify that the checker figures out the value
of the operand even when it's not constant (i.e., makes use of
inlining and constant propagation information). */
int i (int x) { return x; }
const char* s (const char *str) { return str; }
/* Function to "generate" a unique unknown number (as far as GCC can
tell) each time it's called. It prevents the optimizer from being
able to narrow down the ranges of possible values in test functions
with repeated references to the same variable. */
extern int value (void);
/* Return a value in the range [MIN, MAX]. */
int range (int min, int max)
{
T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */
T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */
T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */
T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */
T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */
T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
T1 ("%*.a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T1 ("%*.0a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T1 ("%*.1a", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T1 ("%*.2a", w); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
T1 ("%.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%1.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%2.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T1 ("%3.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
int val = value ();
return val < min || max < val ? min : val;
}
/* Exercise %e. */
void test_e (int w, int p, double x)
#define R(min, max) range (min, max)
/* Verify that the checker can detect buffer overflow when the "%s"
argument is in a known range of lengths and one or both of which
exceed the size of the destination. */
void test_sprintf_chk_string (const char *s)
{
T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */
T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */
T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */
T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */
T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */
T (1, "%*s", R (0, 1), ""); /* { dg-warning "may write a terminating nul" } */
T (1, "%*s", R (-2, -1), ""); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%*s", R (-3, 2), ""); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*s", R (-4, 5), ""); /* { dg-warning "writing up to 5 bytes" } */
T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
T (1, "%*s", R ( -5, 6), "1"); /* { dg-warning "writing between 1 and 6 bytes" } */
T (1, "%*s", R ( -6, 7), "12"); /* { dg-warning "writing between 2 and 7 bytes" } */
T1 ("%*.e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
T1 ("%*.0e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
T1 ("%*.1e", w); /* { dg-warning "writing between 7 and 2147483648 bytes" } */
T1 ("%*.2e", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T (1, "%.*s", R (0, 1), "");
T (1, "%.*s", R (0, 1), s); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*s", R (-2, -1), "");
T (1, "%.*s", R (-2, -1), s); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*s", R (-3, 2), "");
T (1, "%.*s", R (-4, 5), "");
T1 ("%.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%1.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%2.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T1 ("%3.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T (1, "%.*s", R ( -5, 6), "1"); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*s", R ( -6, 7), "12"); /* { dg-warning "writing up to 2 bytes " } */
T (1, "%.*s", R ( 1, 7), "12"); /* { dg-warning "writing between 1 and 2 bytes " } */
T (1, "%.*s", R ( 2, 7), "12"); /* { dg-warning "writing 2 bytes " } */
T2 ("%*.*e", w, p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T (1, "%*.*s", R (0, 1), R (0, 1), ""); /* { dg-warning "may write a terminating nul" } */
T (1, "%*.*s", R (0, 2), R (0, 1), ""); /* { dg-warning "directive writing up to 2 bytes into a region of size 1" } */
T (1, "%*.*s", R (0, 3), R (0, 1), ""); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 1), R (0, 1), "1"); /* { dg-warning "may write a terminating nul" } */
T (1, "%*.*s", R (0, 2), R (0, 1), "1"); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 1), "1"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 1), R (0, 1), "12"); /* { dg-warning "may write a terminating nul" } */
T (1, "%*.*s", R (0, 2), R (0, 1), "12"); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 1), "12"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 1), R (0, 1), "123"); /* { dg-warning "may write a terminating nul" } */
T (1, "%*.*s", R (0, 2), R (0, 1), "123"); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 1), "123"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 1), s); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 1), R (0, 2), "123"); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%*.*s", R (0, 2), R (0, 2), "123"); /* { dg-warning "writing up to 2 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 2), "123"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 2), s); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 1), R (0, 3), "123"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 2), R (0, 3), "123"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 3), "123"); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (0, 3), R (0, 3), s); /* { dg-warning "writing up to 3 bytes" } */
T (1, "%*.*s", R (1, 1), R (0, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 2), R (0, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 3), R (0, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 3), R (0, 3), s); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 1), R (1, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 2), R (1, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 3), R (1, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (1, 3), R (1, 3), s); /* { dg-warning "writing between 1 and 3 bytes" } */
T (1, "%*.*s", R (2, 3), R (1, 3), "123"); /* { dg-warning "writing between 2 and 3 bytes" } */
T (1, "%*.*s", R (3, 4), R (1, 3), "123"); /* { dg-warning "writing between 3 and 4 bytes" } */
T (1, "%*.*s", R (4, 5), R (1, 3), "123"); /* { dg-warning "writing between 4 and 5 bytes" } */
T (1, "%*.*s", R (2, 3), R (1, 3), s); /* { dg-warning "writing between 2 and 3 bytes" } */
}
/* Exercise %f. */
void test_f (int w, int p, double x)
void test_sprintf_chk_int (int w, int p, int i)
{
T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */
T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */
T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */
T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */
T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */
T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */
T (1, "%*d", w, 0); /* { dg-warning "may write a terminating nul|directive writing between 1 and \[0-9\]+ bytes" } */
T (1, "%*d", w, i); /* { dg-warning "may write a terminating nul|directive writing between 1 and \[0-9\]+ bytes" } */
T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */
T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */
T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
T (1, "%*d", R (-1, 1), 0); /* { dg-warning "writing a terminating nul" } */
T (1, "%*d", R ( 0, 1), 0); /* { dg-warning "writing a terminating nul" } */
T (1, "%+*d", R ( 0, 1), 0); /* { dg-warning "directive writing 2 bytes" } */
T (1, "%+*u", R ( 0, 1), 0); /* { dg-warning "writing a terminating nul" } */
T (2, "%*d", R (-3, -2), 0); /* { dg-warning "directive writing between 1 and 3 bytes" } */
T (2, "%*d", R (-3, -1), 0); /* { dg-warning "directive writing between 1 and 3 bytes" } */
T (2, "%*d", R (-3, 0), 0); /* { dg-warning "directive writing between 1 and 3 bytes" } */
T (2, "%*d", R (-2, -1), 0); /* { dg-warning "may write a terminating nul" } */
T (2, "%*d", R (-2, 2), 0); /* { dg-warning "may write a terminating nul" } */
T (2, "%*d", R (-1, 2), 0); /* { dg-warning "may write a terminating nul" } */
T (2, "%*d", R ( 0, 2), 0); /* { dg-warning "may write a terminating nul" } */
T (2, "%*d", R ( 1, 2), 0); /* { dg-warning "may write a terminating nul" } */
T1 ("%*.f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
T1 ("%*.0f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
T1 ("%*.1f", w); /* { dg-warning "writing between 3 and 2147483648 bytes" } */
T1 ("%*.2f", w); /* { dg-warning "writing between 4 and 2147483648 bytes" } */
T1 ("%.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T1 ("%1.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T1 ("%2.*f", p); /* { dg-warning "writing between 2 and 2147483958 bytes" } */
T1 ("%3.*f", p); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T (1, "%.*d", p, 0); /* { dg-warning "may write a terminating nul|directive writing up to \[0-9\]+ bytes" } */
T (1, "%.*d", p, i); /* { dg-warning "may write a terminating nul||directive writing up to \[0-9\]+ bytes" } */
T (1, "%.*d", R (INT_MIN, -1), 0); /* { dg-warning "writing a terminating nul" } */
T (1, "%.*d", R (INT_MIN, 0), 0); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*d", R (-2, -1), 0); /* { dg-warning "writing a terminating nul" } */
T (1, "%.*d", R (-1, 1), 0); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*d", R ( 0, 1), 0); /* { dg-warning "may write a terminating nul" } */
T (1, "%.*d", R ( 0, 2), 0); /* { dg-warning "directive writing up to 2 bytes" } */
T (1, "%.*d", R ( 0, INT_MAX - 1), 0); /* { dg-warning "directive writing up to \[0-9\]+ bytes" } */
T (1, "%.*d", R ( 1, INT_MAX - 1), 0); /* { dg-warning "directive writing between 1 and \[0-9\]+ bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
void test_g (double x)
{
T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */
T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */
T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */
T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */
T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */
T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */
T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */
}
/* Exercise %a. */
void test_a_va (va_list va)
{
T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */
T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */
T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */
T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */
T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */
T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
T ("%*.a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T ("%*.0a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
T ("%*.1a"); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
T ("%*.2a"); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
T ("%.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%1.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%2.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%6.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
T ("%9.*a"); /* { dg-warning "writing between 9 and 2147483658 bytes" } */
T ("%*.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
}
/* Exercise %e. */
void test_e_va (va_list va)
{
T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */
T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */
T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */
T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */
T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */
T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
T ("%.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T ("%1.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
T ("%6.*e"); /* { dg-warning "writing between 6 and 2147483655 bytes" } */
T ("%9.*e"); /* { dg-warning "writing between 9 and 2147483655 bytes" } */
T ("%*.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
}
/* Exercise %f. */
void test_f_va (va_list va)
{
T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */
T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */
T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */
T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */
T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */
T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */
T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */
T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
T ("%.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T ("%1.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
T ("%3.*f"); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
T ("%*.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
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" } */
T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */
T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */
T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */
T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */
T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */
T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */
T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */
T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
T ("%.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
T ("%1.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
T ("%4.*g"); /* { dg-warning "writing between 4 and 310 bytes" } */
T ("%*.*g"); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
}
/* { dg-prune-output "flag used with .%.. gnu_printf format" } */