#include "config.h"
#include "system.h"
#include "tree.h"
-#include "c-lex.h"
-#include "c-tree.h"
#include "flags.h"
#include "toplev.h"
#include "output.h"
#include "rtl.h"
#include "ggc.h"
#include "expr.h"
+#include "c-common.h"
+#include "defaults.h"
#include "tm_p.h"
-
-#if USE_CPPLIB
+#include "intl.h"
+#include "diagnostic.h"
+#include "obstack.h"
#include "cpplib.h"
cpp_reader parse_in;
-cpp_options parse_options;
-enum cpp_token cpp_token;
-#endif
#undef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE TYPE_PRECISION (wchar_type_node)
+#ifndef WINT_TYPE
+#define WINT_TYPE "unsigned int"
+#endif
+
+#ifndef INTMAX_TYPE
+#define INTMAX_TYPE ((INT_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \
+ ? "int" \
+ : ((LONG_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \
+ ? "long int" \
+ : "long long int"))
+#endif
+
+#ifndef UINTMAX_TYPE
+#define UINTMAX_TYPE ((INT_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \
+ ? "unsigned int" \
+ : ((LONG_TYPE_SIZE == LONG_LONG_TYPE_SIZE) \
+ ? "long unsigned int" \
+ : "long long unsigned int"))
+#endif
+
/* The following symbols are subsumed in the c_global_trees array, and
listed here individually for documentation purposes.
tree void_list_node;
+ The identifiers __FUNCTION__, __PRETTY_FUNCTION__, and __func__.
+
+ tree function_id_node;
+ tree pretty_function_id_node;
+ tree func_id_node;
+
*/
tree c_global_trees[CTI_MAX];
+/* Nonzero means don't recognize the non-ANSI builtin functions. */
+
+int flag_no_builtin;
+
+/* Nonzero means don't recognize the non-ANSI builtin functions.
+ -ansi sets this. */
+
+int flag_no_nonansi_builtin;
+
+/* If non-NULL, dump the tree structure for the entire translation
+ unit to this file. */
+
+const char *flag_dump_translation_unit;
+
+/* Warn about *printf or *scanf format/argument anomalies. */
+
+int warn_format;
+
+/* Warn about Y2K problems with strftime formats. */
+
+int warn_format_y2k;
+
+/* Warn about excess arguments to formats. */
+
+int warn_format_extra_args;
+
+/* Warn about non-literal format arguments. */
+
+int warn_format_nonliteral;
+
+/* Nonzero means warn about possible violations of sequence point rules. */
+
+int warn_sequence_point;
+
+/* The elements of `ridpointers' are identifier nodes for the reserved
+ type names and storage classes. It is indexed by a RID_... value. */
+tree *ridpointers;
+
tree (*make_fname_decl) PARAMS ((tree, const char *, int));
+/* If non-NULL, the address of a language-specific function that
+ returns 1 for language-specific statement codes. */
+int (*lang_statement_code_p) PARAMS ((enum tree_code));
+
+/* If non-NULL, the address of a language-specific function that takes
+ any action required right before expand_function_end is called. */
+void (*lang_expand_function_end) PARAMS ((void));
+
+/* If this variable is defined to a non-NULL value, it will be called
+ after the file has been completely parsed. */
+void (*back_end_hook) PARAMS ((tree));
+
/* Nonzero means the expression being parsed will never be evaluated.
This is a count, since unevaluated expressions can nest. */
int skip_evaluation;
int line;
const char *file;
int needs_warning;
+ tree if_stmt;
} if_elt;
-static void tfaff PARAMS ((void));
static if_elt *if_stack;
/* Stack pointer. */
static int if_stack_pointer = 0;
-/* Generate RTL for the start of an if-then, and record the start of it
+/* Record the start of an if-then, and record the start of it
for ambiguous else detection. */
void
-c_expand_start_cond (cond, exitflag, compstmt_count)
+c_expand_start_cond (cond, compstmt_count)
tree cond;
- int exitflag;
int compstmt_count;
{
+ tree if_stmt;
+
/* Make sure there is enough space on the stack. */
if (if_stack_space == 0)
{
if_stack = (if_elt *)xrealloc (if_stack, if_stack_space * sizeof (if_elt));
}
+ if_stmt = build_stmt (IF_STMT, NULL_TREE, NULL_TREE, NULL_TREE);
+ IF_COND (if_stmt) = cond;
+ add_stmt (if_stmt);
+
/* Record this if statement. */
if_stack[if_stack_pointer].compstmt_count = compstmt_count;
if_stack[if_stack_pointer].file = input_filename;
if_stack[if_stack_pointer].line = lineno;
if_stack[if_stack_pointer].needs_warning = 0;
+ if_stack[if_stack_pointer].if_stmt = if_stmt;
if_stack_pointer++;
+}
+
+/* Called after the then-clause for an if-statement is processed. */
- expand_start_cond (cond, exitflag);
+void
+c_finish_then ()
+{
+ tree if_stmt = if_stack[if_stack_pointer - 1].if_stmt;
+ RECHAIN_STMTS (if_stmt, THEN_CLAUSE (if_stmt));
}
-/* Generate RTL for the end of an if-then. Optionally warn if a nested
+/* Record the end of an if-then. Optionally warn if a nested
if statement had an ambiguous else clause. */
void
warning_with_file_and_line (if_stack[if_stack_pointer].file,
if_stack[if_stack_pointer].line,
"suggest explicit braces to avoid ambiguous `else'");
- expand_end_cond ();
+ last_expr_type = NULL_TREE;
}
-/* Generate RTL between the then-clause and the else-clause
+/* Called between the then-clause and the else-clause
of an if-then-else. */
void
case. Also don't warn for any if statements nested in this else. */
if_stack[if_stack_pointer - 1].needs_warning = 0;
if_stack[if_stack_pointer - 1].compstmt_count--;
+}
+
+/* Called after the else-clause for an if-statement is processed. */
- expand_start_else ();
+void
+c_finish_else ()
+{
+ tree if_stmt = if_stack[if_stack_pointer - 1].if_stmt;
+ RECHAIN_STMTS (if_stmt, ELSE_CLAUSE (if_stmt));
}
/* Make bindings for __FUNCTION__, __PRETTY_FUNCTION__, and __func__. */
else
name = "";
printable_name = (*decl_printable_name) (current_function_decl, 2);
+
+ /* ISO C99 defines __func__, which is a variable, not a string
+ constant, and which is not a defined symbol at file scope. */
+ (*make_fname_decl) (func_id_node, name, 0);
}
- (*make_fname_decl) (get_identifier ("__FUNCTION__"), name, 0);
- (*make_fname_decl) (get_identifier ("__PRETTY_FUNCTION__"), printable_name, 1);
- /* The ISO C people "of course" couldn't use __FUNCTION__ in the
- ISO C 99 standard; instead a new variable is invented. */
- (*make_fname_decl) (get_identifier ("__func__"), name, 0);
+ (*make_fname_decl) (function_id_node, name, 0);
+ (*make_fname_decl) (pretty_function_id_node, printable_name, 1);
}
/* Given a chain of STRING_CST nodes,
int wide_flag = 0;
int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT;
int nchars;
+ const int nchars_max = flag_isoc99 ? 4095 : 509;
if (TREE_CHAIN (strings))
{
if (wide_flag)
length = length * wchar_bytes + wide_length;
- p = ggc_alloc_string (NULL, length);
+ p = alloca (length);
/* Copy the individual strings into the new combined string.
If the combined string is wide, convert the chars to ints
else
*q = 0;
- value = make_node (STRING_CST);
- TREE_STRING_POINTER (value) = p;
- TREE_STRING_LENGTH (value) = length;
+ value = build_string (length, p);
}
else
{
/* Compute the number of elements, for the array type. */
nchars = wide_flag ? length / wchar_bytes : length;
+ if (pedantic && nchars > nchars_max && c_language == clk_c)
+ pedwarn ("string length `%d' is greater than the minimum length `%d' ISO C%d is required to support",
+ nchars, nchars_max, flag_isoc99 ? 99 : 89);
+
/* Create the array type for the string constant.
-Wwrite-strings says make the string constant an array of const char
so that copying it to a non-const pointer will get a warning.
error ("no data type for mode `%s'", p);
else
{
+ if (TYPE_PRECISION (typefm) > (TREE_UNSIGNED (type)
+ ? TYPE_PRECISION(uintmax_type_node)
+ : TYPE_PRECISION(intmax_type_node))
+ && pedantic)
+ pedwarn ("type with more precision than %s",
+ TREE_UNSIGNED (type) ? "uintmax_t" : "intmax_t");
TREE_TYPE (decl) = type = typefm;
DECL_SIZE (decl) = DECL_SIZE_UNIT (decl) = 0;
layout_decl (decl, 0);
error ("requested alignment is too large");
else if (is_type)
{
+ /* If we have a TYPE_DECL, then copy the type, so that we
+ don't accidentally modify a builtin type. See pushdecl. */
+ if (decl && TREE_TYPE (decl) != error_mark_node
+ && DECL_ORIGINAL_TYPE (decl) == NULL_TREE)
+ {
+ tree tt = TREE_TYPE (decl);
+ DECL_ORIGINAL_TYPE (decl) = tt;
+ tt = build_type_copy (tt);
+ TYPE_NAME (tt) = decl;
+ TREE_USED (tt) = TREE_USED (decl);
+ TREE_TYPE (decl) = tt;
+ type = tt;
+ }
+
TYPE_ALIGN (type) = (1 << i) * BITS_PER_UNIT;
TYPE_USER_ALIGN (type) = 1;
}
}
}
+ if (format_type == strftime_format_type && first_arg_num != 0)
+ {
+ error ("strftime formats cannot format arguments");
+ continue;
+ }
+
record_function_format (DECL_NAME (decl),
DECL_ASSEMBLER_NAME (decl),
format_type, format_num, first_arg_num);
/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
a parameter list. */
+/* The meaningfully distinct length modifiers for format checking recognised
+ by GCC. */
+enum format_lengths
+{
+ FMT_LEN_none,
+ FMT_LEN_hh,
+ FMT_LEN_h,
+ FMT_LEN_l,
+ FMT_LEN_ll,
+ FMT_LEN_L,
+ FMT_LEN_z,
+ FMT_LEN_t,
+ FMT_LEN_j,
+ FMT_LEN_MAX
+};
+
+
+/* The standard versions in which various format features appeared. */
+enum format_std_version
+{
+ STD_C89,
+ STD_C94,
+ STD_C99,
+ STD_EXT
+};
+
+/* The C standard version C++ is treated as equivalent to
+ or inheriting from, for the purpose of format features supported. */
+#define CPLUSPLUS_STD_VER STD_C89
+/* The C standard version we are checking formats against when pedantic. */
+#define C_STD_VER (c_language == clk_cplusplus \
+ ? CPLUSPLUS_STD_VER \
+ : (flag_isoc99 \
+ ? STD_C99 \
+ : (flag_isoc94 ? STD_C94 : STD_C89)))
+/* The name to give to the standard version we are warning about when
+ pedantic. FEATURE_VER is the version in which the feature warned out
+ appeared, which is higher than C_STD_VER. */
+#define C_STD_NAME(FEATURE_VER) (c_language == clk_cplusplus \
+ ? "ISO C++" \
+ : ((FEATURE_VER) == STD_EXT \
+ ? "ISO C" \
+ : "ISO C89"))
+
+/* Flags that may apply to a particular kind of format checked by GCC. */
+enum
+{
+ /* This format converts arguments of types determined by the
+ format string. */
+ FMT_FLAG_ARG_CONVERT = 1,
+ /* The scanf allocation 'a' kludge applies to this format kind. */
+ FMT_FLAG_SCANF_A_KLUDGE = 2,
+ /* A % during parsing a specifier is allowed to be a modified % rather
+ that indicating the format is broken and we are out-of-sync. */
+ FMT_FLAG_FANCY_PERCENT_OK = 4,
+ /* With $ operand numbers, it is OK to reference the same argument more
+ than once. */
+ FMT_FLAG_DOLLAR_MULTIPLE = 8
+ /* Not included here: details of whether width or precision may occur
+ (controlled by width_char and precision_char); details of whether
+ '*' can be used for these (width_type and precision_type); details
+ of whether length modifiers can occur (length_char_specs); details
+ of when $ operand numbers are allowed (always, for the formats
+ supported, if arguments are converted). */
+};
+
+
+/* Structure describing a length modifier supported in format checking, and
+ possibly a doubled version such as "hh". */
+typedef struct
+{
+ /* Name of the single-character length modifier. */
+ const char *name;
+ /* Index into a format_char_info.types array. */
+ enum format_lengths index;
+ /* Standard version this length appears in. */
+ enum format_std_version std;
+ /* Same, if the modifier can be repeated, or NULL if it can't. */
+ const char *double_name;
+ enum format_lengths double_index;
+ enum format_std_version double_std;
+} format_length_info;
+
+
+/* Structure desribing the combination of a conversion specifier
+ (or a set of specifiers which act identically) and a length modifier. */
+typedef struct
+{
+ /* The standard version this combination of length and type appeared in.
+ This is only relevant if greater than those for length and type
+ individually; otherwise it is ignored. */
+ enum format_std_version std;
+ /* The name to use for the type, if different from that generated internally
+ (e.g., "signed size_t"). */
+ const char *name;
+ /* The type itself. */
+ tree *type;
+} format_type_detail;
+
+
+/* Macros to fill out tables of these. */
+#define BADLEN { 0, NULL, NULL }
+#define NOLENGTHS { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
+
+
+/* Structure desribing a format conversion specifier (or a set of specifiers
+ which act identically), and the length modifiers used with it. */
+typedef struct
+{
+ const char *format_chars;
+ int pointer_count;
+ enum format_std_version std;
+ /* Types accepted for each length modifier. */
+ format_type_detail types[FMT_LEN_MAX];
+ /* List of other modifier characters allowed with these specifiers.
+ This lists flags, and additionally "w" for width, "p" for precision,
+ "a" for scanf "a" allocation extension (not applicable in C99 mode),
+ "*" for scanf suppression, and "E" and "O" for those strftime
+ modifiers. */
+ const char *flag_chars;
+ /* List of additional flags describing these conversion specifiers.
+ "c" for generic character pointers being allowed, "2" for strftime
+ two digit year formats, "3" for strftime formats giving two digit
+ years in some locales, "4" for "2" which becomes "3" with an "E" modifier,
+ "o" if use of strftime "O" is a GNU extension beyond C99,
+ "W" if the argument is a pointer which is dereferenced and written into,
+ "i" for printf integer formats where the '0' flag is ignored with
+ precision, and "[" for the starting character of a scanf scanset. */
+ const char *flags2;
+} format_char_info;
+
+
+/* Structure describing a flag accepted by some kind of format. */
+typedef struct
+{
+ /* The flag character in question (0 for end of array). */
+ int flag_char;
+ /* Zero if this entry describes the flag character in general, or a
+ non-zero character that may be found in flags2 if it describes the
+ flag when used with certain formats only. If the latter, only
+ the first such entry found that applies to the current conversion
+ specifier is used; the values of `name' and `long_name' it supplies
+ will be used, if non-NULL and the standard version is higher than
+ the unpredicated one, for any pedantic warning. For example, 'o'
+ for strftime formats (meaning 'O' is an extension over C99). */
+ int predicate;
+ /* The name to use for this flag in diagnostic messages. For example,
+ N_("`0' flag"), N_("field width"). */
+ const char *name;
+ /* Long name for this flag in diagnostic messages; currently only used for
+ "ISO C does not support ...". For example, N_("the `I' printf flag"). */
+ const char *long_name;
+ /* The standard version in which it appeared. */
+ enum format_std_version std;
+} format_flag_spec;
+
+
+/* Structure describing a combination of flags that is bad for some kind
+ of format. */
+typedef struct
+{
+ /* The first flag character in question (0 for end of array). */
+ int flag_char1;
+ /* The second flag character. */
+ int flag_char2;
+ /* Non-zero if the message should say that the first flag is ignored with
+ the second, zero if the combination should simply be objected to. */
+ int ignored;
+ /* Zero if this entry applies whenever this flag combination occurs,
+ a non-zero character from flags2 if it only applies in some
+ circumstances (e.g. 'i' for printf formats ignoring 0 with precision). */
+ int predicate;
+} format_flag_pair;
+
+
+/* Structure describing a particular kind of format processed by GCC. */
+typedef struct
+{
+ /* The name of this kind of format, for use in diagnostics. */
+ const char *name;
+ /* Specifications of the length modifiers accepted; possibly NULL. */
+ const format_length_info *length_char_specs;
+ /* Details of the conversion specification characters accepted. */
+ const format_char_info *conversion_specs;
+ /* String listing the flag characters that are accepted. */
+ const char *flag_chars;
+ /* String listing modifier characters (strftime) accepted. May be NULL. */
+ const char *modifier_chars;
+ /* Details of the flag characters, including pseudo-flags. */
+ const format_flag_spec *flag_specs;
+ /* Details of bad combinations of flags. */
+ const format_flag_pair *bad_flag_pairs;
+ /* Flags applicable to this kind of format. */
+ int flags;
+ /* Flag character to treat a width as, or 0 if width not used. */
+ int width_char;
+ /* Flag character to treat a precision as, or 0 if precision not used. */
+ int precision_char;
+ /* If a flag character has the effect of suppressing the conversion of
+ an argument ('*' in scanf), that flag character, otherwise 0. */
+ int suppression_char;
+ /* Flag character to treat a length modifier as (ignored if length
+ modifiers not used). Need not be placed in flag_chars for conversion
+ specifiers, but is used to check for bad combinations such as length
+ modifier with assignment suppression in scanf. */
+ int length_code_char;
+ /* Pointer to type of argument expected if '*' is used for a width,
+ or NULL if '*' not used for widths. */
+ tree *width_type;
+ /* Pointer to type of argument expected if '*' is used for a precision,
+ or NULL if '*' not used for precisions. */
+ tree *precision_type;
+} format_kind_info;
+
+
+/* Structure describing details of a type expected in format checking,
+ and the type to check against it. */
+typedef struct format_wanted_type
+{
+ /* The type wanted. */
+ tree wanted_type;
+ /* The name of this type to use in diagnostics. */
+ const char *wanted_type_name;
+ /* The level of indirection through pointers at which this type occurs. */
+ int pointer_count;
+ /* Whether, when pointer_count is 1, to allow any character type when
+ pedantic, rather than just the character or void type specified. */
+ int char_lenient_flag;
+ /* Whether the argument, dereferenced once, is written into and so the
+ argument must not be a pointer to a const-qualified type. */
+ int writing_in_flag;
+ /* If warnings should be of the form "field precision is not type int",
+ the name to use (in this case "field precision"), otherwise NULL,
+ for "%s format, %s arg" type messages. If (in an extension), this
+ is a pointer type, wanted_type_name should be set to include the
+ terminating '*' characters of the type name to give a correct
+ message. */
+ const char *name;
+ /* The actual parameter to check against the wanted type. */
+ tree param;
+ /* The argument number of that parameter. */
+ int arg_num;
+ /* The next type to check for this format conversion, or NULL if none. */
+ struct format_wanted_type *next;
+} format_wanted_type;
+
+
+static const format_length_info printf_length_specs[] =
+{
+ { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
+ { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C99 },
+ { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
+ { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
+ { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
+ { "Z", FMT_LEN_z, STD_EXT, NULL, 0, 0 },
+ { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
+ { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
+ { NULL, 0, 0, NULL, 0, 0 }
+};
+
+
+/* This differs from printf_length_specs only in that "Z" is not accepted. */
+static const format_length_info scanf_length_specs[] =
+{
+ { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
+ { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C99 },
+ { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
+ { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
+ { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
+ { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
+ { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
+ { NULL, 0, 0, NULL, 0, 0 }
+};
+
+
+static const format_flag_spec printf_flag_specs[] =
+{
+ { ' ', 0, N_("` ' flag"), N_("the ` ' printf flag"), STD_C89 },
+ { '+', 0, N_("`+' flag"), N_("the `+' printf flag"), STD_C89 },
+ { '#', 0, N_("`#' flag"), N_("the `#' printf flag"), STD_C89 },
+ { '0', 0, N_("`0' flag"), N_("the `0' printf flag"), STD_C89 },
+ { '-', 0, N_("`-' flag"), N_("the `-' printf flag"), STD_C89 },
+ { '\'', 0, N_("`'' flag"), N_("the `'' printf flag"), STD_EXT },
+ { 'I', 0, N_("`I' flag"), N_("the `I' printf flag"), STD_EXT },
+ { 'w', 0, N_("field width"), N_("field width in printf format"), STD_C89 },
+ { 'p', 0, N_("precision"), N_("precision in printf format"), STD_C89 },
+ { 'L', 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+ { 0, 0, NULL, NULL, 0 }
+};
+
+
+static const format_flag_pair printf_flag_pairs[] =
+{
+ { ' ', '+', 1, 0 },
+ { '0', '-', 1, 0 },
+ { '0', 'p', 1, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+
+static const format_flag_spec scanf_flag_specs[] =
+{
+ { '*', 0, N_("assignment suppression"), N_("assignment suppression"), STD_C89 },
+ { 'a', 0, N_("`a' flag"), N_("the `a' scanf flag"), STD_EXT },
+ { 'w', 0, N_("field width"), N_("field width in scanf format"), STD_C89 },
+ { 'L', 0, N_("length modifier"), N_("length modifier in scanf format"), STD_C89 },
+ { '\'', 0, N_("`'' flag"), N_("the `'' scanf flag"), STD_EXT },
+ { 'I', 0, N_("`I' flag"), N_("the `I' scanf flag"), STD_EXT },
+ { 0, 0, NULL, NULL, 0 }
+};
+
+
+static const format_flag_pair scanf_flag_pairs[] =
+{
+ { '*', 'L', 0, 0 },
+ { 0, 0, 0, 0 }
+};
+
+
+static const format_flag_spec strftime_flag_specs[] =
+{
+ { '_', 0, N_("`_' flag"), N_("the `_' strftime flag"), STD_EXT },
+ { '-', 0, N_("`-' flag"), N_("the `-' strftime flag"), STD_EXT },
+ { '0', 0, N_("`0' flag"), N_("the `0' strftime flag"), STD_EXT },
+ { '^', 0, N_("`^' flag"), N_("the `^' strftime flag"), STD_EXT },
+ { '#', 0, N_("`#' flag"), N_("the `#' strftime flag"), STD_EXT },
+ { 'w', 0, N_("field width"), N_("field width in strftime format"), STD_EXT },
+ { 'E', 0, N_("`E' modifier"), N_("the `E' strftime modifier"), STD_C99 },
+ { 'O', 0, N_("`O' modifier"), N_("the `O' strftime modifier"), STD_C99 },
+ { 'O', 'o', NULL, N_("the `O' modifier"), STD_EXT },
+ { 0, 0, NULL, NULL, 0 }
+};
+
+
+static const format_flag_pair strftime_flag_pairs[] =
+{
+ { 'E', 'O', 0, 0 },
+ { '_', '-', 0, 0 },
+ { '_', '0', 0, 0 },
+ { '-', '0', 0, 0 },
+ { '^', '#', 0, 0 },
+ { 0, 0, 0, 0 }
+};
+
+
#define T_I &integer_type_node
+#define T89_I { STD_C89, NULL, T_I }
+#define T99_I { STD_C99, NULL, T_I }
#define T_L &long_integer_type_node
+#define T89_L { STD_C89, NULL, T_L }
#define T_LL &long_long_integer_type_node
+#define T99_LL { STD_C99, NULL, T_LL }
+#define TEX_LL { STD_EXT, NULL, T_LL }
#define T_S &short_integer_type_node
+#define T89_S { STD_C89, NULL, T_S }
#define T_UI &unsigned_type_node
+#define T89_UI { STD_C89, NULL, T_UI }
+#define T99_UI { STD_C99, NULL, T_UI }
#define T_UL &long_unsigned_type_node
+#define T89_UL { STD_C89, NULL, T_UL }
#define T_ULL &long_long_unsigned_type_node
+#define T99_ULL { STD_C99, NULL, T_ULL }
+#define TEX_ULL { STD_EXT, NULL, T_ULL }
#define T_US &short_unsigned_type_node
+#define T89_US { STD_C89, NULL, T_US }
#define T_F &float_type_node
+#define T89_F { STD_C89, NULL, T_F }
+#define T99_F { STD_C99, NULL, T_F }
#define T_D &double_type_node
+#define T89_D { STD_C89, NULL, T_D }
+#define T99_D { STD_C99, NULL, T_D }
#define T_LD &long_double_type_node
+#define T89_LD { STD_C89, NULL, T_LD }
+#define T99_LD { STD_C99, NULL, T_LD }
#define T_C &char_type_node
+#define T89_C { STD_C89, NULL, T_C }
+#define T_SC &signed_char_type_node
+#define T99_SC { STD_C99, NULL, T_SC }
#define T_UC &unsigned_char_type_node
+#define T99_UC { STD_C99, NULL, T_UC }
#define T_V &void_type_node
+#define T89_V { STD_C89, NULL, T_V }
#define T_W &wchar_type_node
-#define T_ST &sizetype
-
-typedef struct {
- const char *format_chars;
- int pointer_count;
- /* Type of argument if no length modifier is used. */
- tree *nolen;
- /* Type of argument if length modifier for shortening to byte is used.
- If NULL, then this modifier is not allowed. */
- tree *hhlen;
- /* Type of argument if length modifier for shortening is used.
- If NULL, then this modifier is not allowed. */
- tree *hlen;
- /* Type of argument if length modifier `l' is used.
- If NULL, then this modifier is not allowed. */
- tree *llen;
- /* Type of argument if length modifier `q' or `ll' is used.
- If NULL, then this modifier is not allowed. */
- tree *qlen;
- /* Type of argument if length modifier `L' is used.
- If NULL, then this modifier is not allowed. */
- tree *bigllen;
- /* Type of argument if length modifiers 'z' or `Z' is used.
- If NULL, then this modifier is not allowed. */
- tree *zlen;
- /* List of other modifier characters allowed with these options. */
- const char *flag_chars;
-} format_char_info;
+#define T94_W { STD_C94, "wchar_t", T_W }
+#define TEX_W { STD_EXT, "wchar_t", T_W }
+#define T_WI &wint_type_node
+#define T94_WI { STD_C94, "wint_t", T_WI }
+#define TEX_WI { STD_EXT, "wint_t", T_WI }
+#define T_ST &c_size_type_node
+#define T99_ST { STD_C99, "size_t", T_ST }
+#define T_SST &signed_size_type_node
+#define T99_SST { STD_C99, "signed size_t", T_SST }
+#define T_PD &ptrdiff_type_node
+#define T99_PD { STD_C99, "ptrdiff_t", T_PD }
+#define T_UPD &unsigned_ptrdiff_type_node
+#define T99_UPD { STD_C99, "unsigned ptrdiff_t", T_UPD }
+#define T_IM &intmax_type_node
+#define T99_IM { STD_C99, "intmax_t", T_IM }
+#define T_UIM &uintmax_type_node
+#define T99_UIM { STD_C99, "uintmax_t", T_UIM }
+
+static const format_char_info print_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "di", 0, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "-wp0 +'I", "i" },
+ { "oxX", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0#", "i" },
+ { "u", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0'I", "i" },
+ { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#'", "" },
+ { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "" },
+ { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "" },
+ { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "c" },
+ { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c" },
+ { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "", "W" },
+ /* C99 conversion specifiers. */
+ { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#'", "" },
+ { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "" },
+ /* X/Open conversion specifiers. */
+ { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "" },
+ { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "" },
+ /* GNU conversion specifiers. */
+ { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "" },
+ { NULL, 0, 0, NOLENGTHS, NULL, NULL }
+};
-static format_char_info print_char_table[] = {
- { "di", 0, T_I, T_I, T_I, T_L, T_LL, T_LL, T_ST, "-wp0 +" },
- { "oxX", 0, T_UI, T_UI, T_UI, T_UL, T_ULL, T_ULL, T_ST, "-wp0#" },
- { "u", 0, T_UI, T_UI, T_UI, T_UL, T_ULL, T_ULL, T_ST, "-wp0" },
-/* A GNU extension. */
- { "m", 0, T_V, NULL, NULL, NULL, NULL, NULL, NULL, "-wp" },
- { "feEgGaA", 0, T_D, NULL, NULL, NULL, NULL, T_LD, NULL, "-wp0 +#" },
- { "c", 0, T_I, NULL, NULL, T_W, NULL, NULL, NULL, "-w" },
- { "C", 0, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "-w" },
- { "s", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, "-wp" },
- { "S", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "-wp" },
- { "p", 1, T_V, NULL, NULL, NULL, NULL, NULL, NULL, "-w" },
- { "n", 1, T_I, NULL, T_S, T_L, T_LL, NULL, NULL, "" },
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+static const format_char_info scan_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "di", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "*w'I", "W" },
+ { "u", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "*w'I", "W" },
+ { "oxX", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "*w", "W" },
+ { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "*w'", "W" },
+ { "c", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "cW" },
+ { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW" },
+ { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW[" },
+ { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W" },
+ { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "", "W" },
+ /* C99 conversion specifiers. */
+ { "FaA", 1, STD_C99, { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "*w'", "W" },
+ /* X/Open conversion specifiers. */
+ { "C", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W" },
+ { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "W" },
+ { NULL, 0, 0, NOLENGTHS, NULL, NULL }
};
-static format_char_info scan_char_table[] = {
- { "di", 1, T_I, T_C, T_S, T_L, T_LL, T_LL, NULL, "*" },
- { "ouxX", 1, T_UI, T_UC, T_US, T_UL, T_ULL, T_ULL, NULL, "*" },
- { "efgEGaA", 1, T_F, NULL, NULL, T_D, NULL, T_LD, NULL, "*" },
- { "c", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, "*" },
- { "s", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, "*a" },
- { "[", 1, T_C, NULL, NULL, NULL, NULL, NULL, NULL, "*a" },
- { "C", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "*" },
- { "S", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "*a" },
- { "p", 2, T_V, NULL, NULL, NULL, NULL, NULL, NULL, "*" },
- { "n", 1, T_I, T_C, T_S, T_L, T_LL, NULL, NULL, "" },
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+static format_char_info time_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "ABZab", 0, STD_C89, NOLENGTHS, "^#", "" },
+ { "cx", 0, STD_C89, NOLENGTHS, "E", "3" },
+ { "HIMSUWdmw", 0, STD_C89, NOLENGTHS, "-_0Ow", "" },
+ { "j", 0, STD_C89, NOLENGTHS, "-_0Ow", "o" },
+ { "p", 0, STD_C89, NOLENGTHS, "#", "" },
+ { "X", 0, STD_C89, NOLENGTHS, "E", "" },
+ { "y", 0, STD_C89, NOLENGTHS, "EO-_0w", "4" },
+ { "Y", 0, STD_C89, NOLENGTHS, "-_0EOw", "o" },
+ { "%", 0, STD_C89, NOLENGTHS, "", "" },
+ /* C99 conversion specifiers. */
+ { "C", 0, STD_C99, NOLENGTHS, "-_0EOw", "o" },
+ { "D", 0, STD_C99, NOLENGTHS, "", "2" },
+ { "eVu", 0, STD_C99, NOLENGTHS, "-_0Ow", "" },
+ { "FRTnrt", 0, STD_C99, NOLENGTHS, "", "" },
+ { "g", 0, STD_C99, NOLENGTHS, "O-_0w", "2o" },
+ { "G", 0, STD_C99, NOLENGTHS, "-_0Ow", "o" },
+ { "h", 0, STD_C99, NOLENGTHS, "^#", "" },
+ { "z", 0, STD_C99, NOLENGTHS, "O", "o" },
+ /* GNU conversion specifiers. */
+ { "kls", 0, STD_EXT, NOLENGTHS, "-_0Ow", "" },
+ { "P", 0, STD_EXT, NOLENGTHS, "", "" },
+ { NULL, 0, 0, NOLENGTHS, NULL, NULL }
};
-/* Handle format characters recognized by glibc's strftime.c.
- '2' - MUST do years as only two digits
- '3' - MAY do years as only two digits (depending on locale)
- 'E' - E modifier is acceptable
- 'O' - O modifier is acceptable to Standard C
- 'o' - O modifier is acceptable as a GNU extension
- 'G' - other GNU extensions */
-
-static format_char_info time_char_table[] = {
- { "y", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
- { "D", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
- { "g", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
- { "cx", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
- { "%RTXnrt", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "" },
- { "P", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
- { "HIMSUWdemw", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
- { "Vju", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
- { "Gklsz", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
- { "ABZa", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
- { "p", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
- { "bh", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
- { "CY", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+/* This must be in the same order as enum format_type. */
+static const format_kind_info format_types[] =
+{
+ { "printf", printf_length_specs, print_char_table, " +#0-'I", NULL,
+ printf_flag_specs, printf_flag_pairs,
+ FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE, 'w', 'p', 0, 'L',
+ &integer_type_node, &integer_type_node
+ },
+ { "scanf", scanf_length_specs, scan_char_table, "*'I", NULL,
+ scanf_flag_specs, scanf_flag_pairs,
+ FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE, 'w', 0, '*', 'L',
+ NULL, NULL
+ },
+ { "strftime", NULL, time_char_table, "_-0^#", "EO",
+ strftime_flag_specs, strftime_flag_pairs,
+ FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0,
+ NULL, NULL
+ }
};
+
typedef struct function_format_info
{
struct function_format_info *next; /* next structure on the list */
static international_format_info *international_format_list = NULL;
-static void check_format_info PARAMS ((function_format_info *, tree));
+/* Structure detailing the results of checking a format function call
+ where the format expression may be a conditional expression with
+ many leaves resulting from nested conditional expressions. */
+typedef struct
+{
+ /* Number of leaves of the format argument that could not be checked
+ as they were not string literals. */
+ int number_non_literal;
+ /* Number of leaves of the format argument that were null pointers or
+ string literals, but had extra format arguments. */
+ int number_extra_args;
+ /* Number of leaves of the format argument that were null pointers or
+ string literals, but had extra format arguments and used $ operand
+ numbers. */
+ int number_dollar_extra_args;
+ /* Number of leaves of the format argument that were wide string
+ literals. */
+ int number_wide;
+ /* Number of leaves of the format argument that were empty strings. */
+ int number_empty;
+ /* Number of leaves of the format argument that were unterminated
+ strings. */
+ int number_unterminated;
+ /* Number of leaves of the format argument that were not counted above. */
+ int number_other;
+} format_check_results;
+
+static void check_format_info PARAMS ((int *, function_format_info *, tree));
+static void check_format_info_recurse PARAMS ((int *, format_check_results *,
+ function_format_info *, tree,
+ tree, int));
+static void check_format_info_main PARAMS ((int *, format_check_results *,
+ function_format_info *,
+ const char *, int, tree, int));
+static void status_warning PARAMS ((int *, const char *, ...))
+ ATTRIBUTE_PRINTF_2;
+
+static void init_dollar_format_checking PARAMS ((int, tree));
+static int maybe_read_dollar_number PARAMS ((int *, const char **, int,
+ tree, tree *,
+ const format_kind_info *));
+static void finish_dollar_format_checking PARAMS ((int *, format_check_results *));
+
+static const format_flag_spec *get_flag_spec PARAMS ((const format_flag_spec *,
+ int, const char *));
+
+static void check_format_types PARAMS ((int *, format_wanted_type *));
+static int is_valid_printf_arglist PARAMS ((tree));
+static rtx c_expand_builtin PARAMS ((tree, rtx, enum machine_mode, enum expand_modifier));
+static rtx c_expand_builtin_printf PARAMS ((tree, rtx, enum machine_mode,
+ enum expand_modifier, int));
/* Initialize the table of functions to perform format checking on.
- The ANSI functions are always checked (whether <stdio.h> is
+ The ISO C functions are always checked (whether <stdio.h> is
included or not), since it is common to call printf without
including <stdio.h>. There shouldn't be a problem with this,
- since ANSI reserves these function names whether you include the
- header file or not. In any case, the checking is harmless.
+ since ISO C reserves these function names whether you include the
+ header file or not. In any case, the checking is harmless. With
+ -ffreestanding, these default attributes are disabled, and must be
+ specified manually if desired.
Also initialize the name of function that modify the format string for
internationalization purposes. */
void
init_function_format_info ()
{
- record_function_format (get_identifier ("printf"), NULL_TREE,
- printf_format_type, 1, 2);
- record_function_format (get_identifier ("fprintf"), NULL_TREE,
- printf_format_type, 2, 3);
- record_function_format (get_identifier ("sprintf"), NULL_TREE,
- printf_format_type, 2, 3);
- record_function_format (get_identifier ("scanf"), NULL_TREE,
- scanf_format_type, 1, 2);
- record_function_format (get_identifier ("fscanf"), NULL_TREE,
- scanf_format_type, 2, 3);
- record_function_format (get_identifier ("sscanf"), NULL_TREE,
- scanf_format_type, 2, 3);
- record_function_format (get_identifier ("vprintf"), NULL_TREE,
- printf_format_type, 1, 0);
- record_function_format (get_identifier ("vfprintf"), NULL_TREE,
- printf_format_type, 2, 0);
- record_function_format (get_identifier ("vsprintf"), NULL_TREE,
- printf_format_type, 2, 0);
- record_function_format (get_identifier ("strftime"), NULL_TREE,
- strftime_format_type, 3, 0);
-
- record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
- record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
- record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
+ if (flag_hosted)
+ {
+ /* Functions from ISO/IEC 9899:1990. */
+ record_function_format (get_identifier ("printf"), NULL_TREE,
+ printf_format_type, 1, 2);
+ record_function_format (get_identifier ("__builtin_printf"), NULL_TREE,
+ printf_format_type, 1, 2);
+ record_function_format (get_identifier ("fprintf"), NULL_TREE,
+ printf_format_type, 2, 3);
+ record_function_format (get_identifier ("sprintf"), NULL_TREE,
+ printf_format_type, 2, 3);
+ record_function_format (get_identifier ("scanf"), NULL_TREE,
+ scanf_format_type, 1, 2);
+ record_function_format (get_identifier ("fscanf"), NULL_TREE,
+ scanf_format_type, 2, 3);
+ record_function_format (get_identifier ("sscanf"), NULL_TREE,
+ scanf_format_type, 2, 3);
+ record_function_format (get_identifier ("vprintf"), NULL_TREE,
+ printf_format_type, 1, 0);
+ record_function_format (get_identifier ("vfprintf"), NULL_TREE,
+ printf_format_type, 2, 0);
+ record_function_format (get_identifier ("vsprintf"), NULL_TREE,
+ printf_format_type, 2, 0);
+ record_function_format (get_identifier ("strftime"), NULL_TREE,
+ strftime_format_type, 3, 0);
+ }
+
+ if (flag_hosted && flag_isoc99)
+ {
+ /* ISO C99 adds the snprintf and vscanf family functions. */
+ record_function_format (get_identifier ("snprintf"), NULL_TREE,
+ printf_format_type, 3, 4);
+ record_function_format (get_identifier ("vsnprintf"), NULL_TREE,
+ printf_format_type, 3, 0);
+ record_function_format (get_identifier ("vscanf"), NULL_TREE,
+ scanf_format_type, 1, 0);
+ record_function_format (get_identifier ("vfscanf"), NULL_TREE,
+ scanf_format_type, 2, 0);
+ record_function_format (get_identifier ("vsscanf"), NULL_TREE,
+ scanf_format_type, 2, 0);
+ }
+
+ if (flag_hosted && flag_noniso_default_format_attributes)
+ {
+ /* Uniforum/GNU gettext functions, not in ISO C. */
+ record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
+ record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
+ record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
+ }
}
/* Record information for argument format checking. FUNCTION_IDENT is
info->format_num = format_num;
}
-
-static void
-tfaff ()
-{
- warning ("too few arguments for format");
-}
\f
/* Check the argument list of a call to printf, scanf, etc.
NAME is the function identifier.
ASSEMBLER_NAME is the function's assembler identifier.
(Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.)
- PARAMS is the list of argument values. */
+ PARAMS is the list of argument values. Also, if -Wmissing-format-attribute,
+ warn for calls to vprintf or vscanf in functions with no such format
+ attribute themselves. */
void
-check_function_format (name, assembler_name, params)
+check_function_format (status, name, assembler_name, params)
+ int *status;
tree name;
tree assembler_name;
tree params;
: (info->name == name))
{
/* Yup; check it. */
- check_format_info (info, params);
+ check_format_info (status, info, params);
+ if (warn_missing_format_attribute && info->first_arg_num == 0
+ && (format_types[info->format_type].flags & FMT_FLAG_ARG_CONVERT))
+ {
+ function_format_info *info2;
+ for (info2 = function_format_list; info2; info2 = info2->next)
+ if ((info2->assembler_name
+ ? (info2->assembler_name == DECL_ASSEMBLER_NAME (current_function_decl))
+ : (info2->name == DECL_NAME (current_function_decl)))
+ && info2->format_type == info->format_type)
+ break;
+ if (info2 == NULL)
+ warning ("function might be possible candidate for `%s' format attribute",
+ format_types[info->format_type].name);
+ }
break;
}
}
}
+/* This function replaces `warning' inside the printf format checking
+ functions. If the `status' parameter is non-NULL, then it is
+ dereferenced and set to 1 whenever a warning is caught. Otherwise
+ it warns as usual by replicating the innards of the warning
+ function from diagnostic.c. */
+static void
+status_warning VPARAMS ((int *status, const char *msgid, ...))
+{
+#ifndef ANSI_PROTOTYPES
+ int *status;
+ const char *msgid;
+#endif
+ va_list ap;
+ diagnostic_context dc;
+
+ VA_START (ap, msgid);
+
+#ifndef ANSI_PROTOTYPES
+ status = va_arg (ap, int *);
+ msgid = va_arg (ap, const char *);
+#endif
+
+ if (status)
+ *status = 1;
+ else
+ {
+ /* This duplicates the warning function behavior. */
+ set_diagnostic_context
+ (&dc, msgid, &ap, input_filename, lineno, /* warn = */ 1);
+ report_diagnostic (&dc);
+ }
+
+ va_end (ap);
+}
+
+/* Variables used by the checking of $ operand number formats. */
+static char *dollar_arguments_used = NULL;
+static int dollar_arguments_alloc = 0;
+static int dollar_arguments_count;
+static int dollar_first_arg_num;
+static int dollar_max_arg_used;
+static int dollar_format_warned;
+
+/* Initialize the checking for a format string that may contain $
+ parameter number specifications; we will need to keep track of whether
+ each parameter has been used. FIRST_ARG_NUM is the number of the first
+ argument that is a parameter to the format, or 0 for a vprintf-style
+ function; PARAMS is the list of arguments starting at this argument. */
+
+static void
+init_dollar_format_checking (first_arg_num, params)
+ int first_arg_num;
+ tree params;
+{
+ dollar_first_arg_num = first_arg_num;
+ dollar_arguments_count = 0;
+ dollar_max_arg_used = 0;
+ dollar_format_warned = 0;
+ if (first_arg_num > 0)
+ {
+ while (params)
+ {
+ dollar_arguments_count++;
+ params = TREE_CHAIN (params);
+ }
+ }
+ if (dollar_arguments_alloc < dollar_arguments_count)
+ {
+ if (dollar_arguments_used)
+ free (dollar_arguments_used);
+ dollar_arguments_alloc = dollar_arguments_count;
+ dollar_arguments_used = xmalloc (dollar_arguments_alloc);
+ }
+ if (dollar_arguments_alloc)
+ memset (dollar_arguments_used, 0, dollar_arguments_alloc);
+}
+
+
+/* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED
+ is set, it is an error if one is not found; otherwise, it is OK. If
+ such a number is found, check whether it is within range and mark that
+ numbered operand as being used for later checking. Returns the operand
+ number if found and within range, zero if no such number was found and
+ this is OK, or -1 on error. PARAMS points to the first operand of the
+ format; PARAM_PTR is made to point to the parameter referred to. If
+ a $ format is found, *FORMAT is updated to point just after it. */
+
+static int
+maybe_read_dollar_number (status, format, dollar_needed, params, param_ptr,
+ fki)
+ int *status;
+ const char **format;
+ int dollar_needed;
+ tree params;
+ tree *param_ptr;
+ const format_kind_info *fki;
+{
+ int argnum;
+ int overflow_flag;
+ const char *fcp = *format;
+ if (*fcp < '0' || *fcp > '9')
+ {
+ if (dollar_needed)
+ {
+ status_warning (status, "missing $ operand number in format");
+ return -1;
+ }
+ else
+ return 0;
+ }
+ argnum = 0;
+ overflow_flag = 0;
+ while (*fcp >= '0' && *fcp <= '9')
+ {
+ int nargnum;
+ nargnum = 10 * argnum + (*fcp - '0');
+ if (nargnum < 0 || nargnum / 10 != argnum)
+ overflow_flag = 1;
+ argnum = nargnum;
+ fcp++;
+ }
+ if (*fcp != '$')
+ {
+ if (dollar_needed)
+ {
+ status_warning (status, "missing $ operand number in format");
+ return -1;
+ }
+ else
+ return 0;
+ }
+ *format = fcp + 1;
+ if (pedantic && !dollar_format_warned)
+ {
+ status_warning (status,
+ "%s does not support %%n$ operand number formats",
+ C_STD_NAME (STD_EXT));
+ dollar_format_warned = 1;
+ }
+ if (overflow_flag || argnum == 0
+ || (dollar_first_arg_num && argnum > dollar_arguments_count))
+ {
+ status_warning (status, "operand number out of range in format");
+ return -1;
+ }
+ if (argnum > dollar_max_arg_used)
+ dollar_max_arg_used = argnum;
+ /* For vprintf-style functions we may need to allocate more memory to
+ track which arguments are used. */
+ while (dollar_arguments_alloc < dollar_max_arg_used)
+ {
+ int nalloc;
+ nalloc = 2 * dollar_arguments_alloc + 16;
+ dollar_arguments_used = xrealloc (dollar_arguments_used, nalloc);
+ memset (dollar_arguments_used + dollar_arguments_alloc, 0,
+ nalloc - dollar_arguments_alloc);
+ dollar_arguments_alloc = nalloc;
+ }
+ if (!(fki->flags & FMT_FLAG_DOLLAR_MULTIPLE)
+ && dollar_arguments_used[argnum - 1] == 1)
+ {
+ dollar_arguments_used[argnum - 1] = 2;
+ status_warning (status,
+ "format argument %d used more than once in %s format",
+ argnum, fki->name);
+ }
+ else
+ dollar_arguments_used[argnum - 1] = 1;
+ if (dollar_first_arg_num)
+ {
+ int i;
+ *param_ptr = params;
+ for (i = 1; i < argnum && *param_ptr != 0; i++)
+ *param_ptr = TREE_CHAIN (*param_ptr);
+
+ if (*param_ptr == 0)
+ {
+ /* This case shouldn't be caught here. */
+ abort ();
+ }
+ }
+ else
+ *param_ptr = 0;
+ return argnum;
+}
+
+
+/* Finish the checking for a format string that used $ operand number formats
+ instead of non-$ formats. We check for unused operands before used ones
+ (a serious error, since the implementation of the format function
+ can't know what types to pass to va_arg to find the later arguments).
+ and for unused operands at the end of the format (if we know how many
+ arguments the format had, so not for vprintf). If there were operand
+ numbers out of range on a non-vprintf-style format, we won't have reached
+ here. */
+
+static void
+finish_dollar_format_checking (status, res)
+ int *status;
+ format_check_results *res;
+{
+ int i;
+ for (i = 0; i < dollar_max_arg_used; i++)
+ {
+ if (!dollar_arguments_used[i])
+ status_warning (status, "format argument %d unused before used argument %d in $-style format",
+ i + 1, dollar_max_arg_used);
+ }
+ if (dollar_first_arg_num && dollar_max_arg_used < dollar_arguments_count)
+ {
+ res->number_other--;
+ res->number_dollar_extra_args++;
+ }
+}
+
+
+/* Retrieve the specification for a format flag. SPEC contains the
+ specifications for format flags for the applicable kind of format.
+ FLAG is the flag in question. If PREDICATES is NULL, the basic
+ spec for that flag must be retrieved and this function aborts if
+ it cannot be found. If PREDICATES is not NULL, it is a string listing
+ possible predicates for the spec entry; if an entry predicated on any
+ of these is found, it is returned, otherwise NULL is returned. */
+
+static const format_flag_spec *
+get_flag_spec (spec, flag, predicates)
+ const format_flag_spec *spec;
+ int flag;
+ const char *predicates;
+{
+ int i;
+ for (i = 0; spec[i].flag_char != 0; i++)
+ {
+ if (spec[i].flag_char != flag)
+ continue;
+ if (predicates != NULL)
+ {
+ if (spec[i].predicate != 0
+ && strchr (predicates, spec[i].predicate) != 0)
+ return &spec[i];
+ }
+ else if (spec[i].predicate == 0)
+ return &spec[i];
+ }
+ if (predicates == NULL)
+ abort ();
+ else
+ return NULL;
+}
+
+
/* Check the argument list of a call to printf, scanf, etc.
INFO points to the function_format_info structure.
PARAMS is the list of argument values. */
static void
-check_format_info (info, params)
+check_format_info (status, info, params)
+ int *status;
function_format_info *info;
tree params;
{
- int i;
int arg_num;
- int suppressed, wide, precise;
- int length_char = 0;
- int format_char;
- int format_length;
tree format_tree;
- tree cur_param;
- tree cur_type;
- tree wanted_type;
- tree first_fillin_param;
- const char *format_chars;
- format_char_info *fci = NULL;
- char flag_chars[8];
- int has_operand_number = 0;
-
+ format_check_results res;
/* Skip to format argument. If the argument isn't available, there's
no work for us to do; prototype checking will catch the problem. */
for (arg_num = 1; ; ++arg_num)
if (format_tree == 0)
return;
- /* We can only check the format if it's a string constant. */
- while (TREE_CODE (format_tree) == NOP_EXPR)
- format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */
+ res.number_non_literal = 0;
+ res.number_extra_args = 0;
+ res.number_dollar_extra_args = 0;
+ res.number_wide = 0;
+ res.number_empty = 0;
+ res.number_unterminated = 0;
+ res.number_other = 0;
- if (TREE_CODE (format_tree) == CALL_EXPR
- && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR
- && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0))
- == FUNCTION_DECL))
+ check_format_info_recurse (status, &res, info, format_tree, params, arg_num);
+
+ if (res.number_non_literal > 0)
{
- tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0);
+ /* Functions taking a va_list normally pass a non-literal format
+ string. These functions typically are declared with
+ first_arg_num == 0, so avoid warning in those cases. */
+ if (info->first_arg_num != 0 && warn_format_nonliteral)
+ status_warning (status, "format not a string literal, argument types not checked");
+ }
- /* See if this is a call to a known internationalization function
- that modifies the format arg. */
- international_format_info *info;
+ /* If there were extra arguments to the format, normally warn. However,
+ the standard does say extra arguments are ignored, so in the specific
+ case where we have multiple leaves (conditional expressions or
+ ngettext) allow extra arguments if at least one leaf didn't have extra
+ arguments, but was otherwise OK (either non-literal or checked OK).
+ If the format is an empty string, this should be counted similarly to the
+ case of extra format arguments. */
+ if (res.number_extra_args > 0 && res.number_non_literal == 0
+ && res.number_other == 0 && warn_format_extra_args)
+ status_warning (status, "too many arguments for format");
+ if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0
+ && res.number_other == 0 && warn_format_extra_args)
+ status_warning (status, "unused arguments in $-style format");
+ if (res.number_empty > 0 && res.number_non_literal == 0
+ && res.number_other == 0)
+ status_warning (status, "zero-length format string");
+
+ if (res.number_wide > 0)
+ status_warning (status, "format is a wide character string");
+
+ if (res.number_unterminated > 0)
+ status_warning (status, "unterminated format string");
+}
+
+
+/* Recursively check a call to a format function. FORMAT_TREE is the
+ format parameter, which may be a conditional expression in which
+ both halves should be checked. ARG_NUM is the number of the
+ format argument; PARAMS points just after it in the argument list. */
+
+static void
+check_format_info_recurse (status, res, info, format_tree, params, arg_num)
+ int *status;
+ format_check_results *res;
+ function_format_info *info;
+ tree format_tree;
+ tree params;
+ int arg_num;
+{
+ int format_length;
+ const char *format_chars;
+ tree array_size = 0;
+ tree array_init;
+
+ if (TREE_CODE (format_tree) == NOP_EXPR)
+ {
+ /* Strip coercion. */
+ check_format_info_recurse (status, res, info,
+ TREE_OPERAND (format_tree, 0), params,
+ arg_num);
+ return;
+ }
+
+ if (TREE_CODE (format_tree) == CALL_EXPR
+ && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR
+ && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0))
+ == FUNCTION_DECL))
+ {
+ tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0);
- for (info = international_format_list; info; info = info->next)
- if (info->assembler_name
- ? (info->assembler_name == DECL_ASSEMBLER_NAME (function))
- : (info->name == DECL_NAME (function)))
+ /* See if this is a call to a known internationalization function
+ that modifies the format arg. */
+ international_format_info *iinfo;
+
+ for (iinfo = international_format_list; iinfo; iinfo = iinfo->next)
+ if (iinfo->assembler_name
+ ? (iinfo->assembler_name == DECL_ASSEMBLER_NAME (function))
+ : (iinfo->name == DECL_NAME (function)))
{
tree inner_args;
int i;
for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
inner_args != 0;
inner_args = TREE_CHAIN (inner_args), i++)
- if (i == info->format_num)
+ if (i == iinfo->format_num)
{
- format_tree = TREE_VALUE (inner_args);
-
- while (TREE_CODE (format_tree) == NOP_EXPR)
- format_tree = TREE_OPERAND (format_tree, 0);
+ /* FIXME: with Marc Espie's __attribute__((nonnull))
+ patch in GCC, we will have chained attributes,
+ and be able to handle functions like ngettext
+ with multiple format_arg attributes properly. */
+ check_format_info_recurse (status, res, info,
+ TREE_VALUE (inner_args), params,
+ arg_num);
+ return;
}
}
}
+ if (TREE_CODE (format_tree) == COND_EXPR)
+ {
+ /* Check both halves of the conditional expression. */
+ check_format_info_recurse (status, res, info,
+ TREE_OPERAND (format_tree, 1), params,
+ arg_num);
+ check_format_info_recurse (status, res, info,
+ TREE_OPERAND (format_tree, 2), params,
+ arg_num);
+ return;
+ }
+
if (integer_zerop (format_tree))
{
- warning ("null format string");
+ /* FIXME: this warning should go away once Marc Espie's
+ __attribute__((nonnull)) patch is in. Instead, checking for
+ nonnull attributes should probably change this function to act
+ specially if info == NULL and add a res->number_null entry for
+ that case, or maybe add a function pointer to be called at
+ the end instead of hardcoding check_format_info_main. */
+ status_warning (status, "null format string");
+
+ /* Skip to first argument to check, so we can see if this format
+ has any arguments (it shouldn't). */
+ while (arg_num + 1 < info->first_arg_num)
+ {
+ if (params == 0)
+ return;
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ }
+
+ if (params == 0)
+ res->number_other++;
+ else
+ res->number_extra_args++;
+
return;
}
+
if (TREE_CODE (format_tree) != ADDR_EXPR)
{
- /* The user may get multiple warnings if the supplied argument
- isn't even a string pointer. */
- /* Functions taking a va_list normally pass a non-literal format
- string. These functions typically are declared with
- first_arg_num == 0, so avoid warning in those cases. */
- if (info->first_arg_num != 0 && warn_format > 1)
- warning ("format not a string literal, argument types not checked");
+ res->number_non_literal++;
return;
}
format_tree = TREE_OPERAND (format_tree, 0);
+ if (TREE_CODE (format_tree) == VAR_DECL
+ && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE
+ && (array_init = decl_constant_value (format_tree)) != format_tree
+ && TREE_CODE (array_init) == STRING_CST)
+ {
+ /* Extract the string constant initializer. Note that this may include
+ a trailing NUL character that is not in the array (e.g.
+ const char a[3] = "foo";). */
+ array_size = DECL_SIZE_UNIT (format_tree);
+ format_tree = array_init;
+ }
if (TREE_CODE (format_tree) != STRING_CST)
{
- /* The user may get multiple warnings if the supplied argument
- isn't even a string pointer. */
- /* Functions taking a va_list normally pass a non-literal format
- string. These functions typically are declared with
- first_arg_num == 0, so avoid warning in those cases. */
- if (info->first_arg_num != 0 && warn_format > 1)
- warning ("format not a string literal, argument types not checked");
+ res->number_non_literal++;
+ return;
+ }
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))) != char_type_node)
+ {
+ res->number_wide++;
return;
}
format_chars = TREE_STRING_POINTER (format_tree);
format_length = TREE_STRING_LENGTH (format_tree);
- if (format_length <= 1)
- warning ("zero-length format string");
+ if (array_size != 0)
+ {
+ /* Variable length arrays can't be initialized. */
+ if (TREE_CODE (array_size) != INTEGER_CST)
+ abort ();
+ if (host_integerp (array_size, 0))
+ {
+ HOST_WIDE_INT array_size_value = TREE_INT_CST_LOW (array_size);
+ if (array_size_value > 0
+ && array_size_value == (int) array_size_value
+ && format_length > array_size_value)
+ format_length = array_size_value;
+ }
+ }
+ if (format_length < 1)
+ {
+ res->number_unterminated++;
+ return;
+ }
+ if (format_length == 1)
+ {
+ res->number_empty++;
+ return;
+ }
if (format_chars[--format_length] != 0)
{
- warning ("unterminated format string");
+ res->number_unterminated++;
return;
}
+
/* Skip to first argument to check. */
while (arg_num + 1 < info->first_arg_num)
{
params = TREE_CHAIN (params);
++arg_num;
}
+ /* Provisionally increment res->number_other; check_format_info_main
+ will decrement it if it finds there are extra arguments, but this way
+ need not adjust it for every return. */
+ res->number_other++;
+ check_format_info_main (status, res, info, format_chars, format_length,
+ params, arg_num);
+}
+
+
+/* Do the main part of checking a call to a format function. FORMAT_CHARS
+ is the NUL-terminated format string (which at this point may contain
+ internal NUL characters); FORMAT_LENGTH is its length (excluding the
+ terminating NUL character). ARG_NUM is one less than the number of
+ the first format argument to check; PARAMS points to that format
+ argument in the list of arguments. */
+
+static void
+check_format_info_main (status, res, info, format_chars, format_length,
+ params, arg_num)
+ int *status;
+ format_check_results *res;
+ function_format_info *info;
+ const char *format_chars;
+ int format_length;
+ tree params;
+ int arg_num;
+{
+ const char *orig_format_chars = format_chars;
+ tree first_fillin_param = params;
+
+ const format_kind_info *fki = &format_types[info->format_type];
+ const format_flag_spec *flag_specs = fki->flag_specs;
+ const format_flag_pair *bad_flag_pairs = fki->bad_flag_pairs;
+
+ /* -1 if no conversions taking an operand have been found; 0 if one has
+ and it didn't use $; 1 if $ formats are in use. */
+ int has_operand_number = -1;
+
+ init_dollar_format_checking (info->first_arg_num, first_fillin_param);
- first_fillin_param = params;
while (1)
{
- int aflag;
+ int i;
+ int suppressed = FALSE;
+ const char *length_chars = NULL;
+ enum format_lengths length_chars_val = FMT_LEN_none;
+ enum format_std_version length_chars_std = STD_C89;
+ int format_char;
+ tree cur_param;
+ tree wanted_type;
+ int main_arg_num = 0;
+ tree main_arg_params = 0;
+ enum format_std_version wanted_type_std;
+ const char *wanted_type_name;
+ format_wanted_type width_wanted_type;
+ format_wanted_type precision_wanted_type;
+ format_wanted_type main_wanted_type;
+ format_wanted_type *first_wanted_type = NULL;
+ format_wanted_type *last_wanted_type = NULL;
+ const format_length_info *fli = NULL;
+ const format_char_info *fci = NULL;
+ char flag_chars[256];
+ int aflag = 0;
if (*format_chars == 0)
{
- if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
- warning ("embedded `\\0' in format");
- if (info->first_arg_num != 0 && params != 0 && ! has_operand_number)
- warning ("too many arguments for format");
+ if (format_chars - orig_format_chars != format_length)
+ status_warning (status, "embedded `\\0' in format");
+ if (info->first_arg_num != 0 && params != 0
+ && has_operand_number <= 0)
+ {
+ res->number_other--;
+ res->number_extra_args++;
+ }
+ if (has_operand_number > 0)
+ finish_dollar_format_checking (status, res);
return;
}
if (*format_chars++ != '%')
continue;
if (*format_chars == 0)
{
- warning ("spurious trailing `%%' in format");
+ status_warning (status, "spurious trailing `%%' in format");
continue;
}
if (*format_chars == '%')
continue;
}
flag_chars[0] = 0;
- suppressed = wide = precise = FALSE;
- if (info->format_type == scanf_format_type)
+
+ if ((fki->flags & FMT_FLAG_ARG_CONVERT) && has_operand_number != 0)
{
- suppressed = *format_chars == '*';
- if (suppressed)
- ++format_chars;
- while (ISDIGIT (*format_chars))
- ++format_chars;
- }
- else if (info->format_type == strftime_format_type)
- {
- while (*format_chars != 0 && index ("_-0^#", *format_chars) != 0)
+ /* Possibly read a $ operand number at the start of the format.
+ If one was previously used, one is required here. If one
+ is not used here, we can't immediately conclude this is a
+ format without them, since it could be printf %m or scanf %*. */
+ int opnum;
+ opnum = maybe_read_dollar_number (status, &format_chars, 0,
+ first_fillin_param,
+ &main_arg_params, fki);
+ if (opnum == -1)
+ return;
+ else if (opnum > 0)
{
- if (pedantic)
- warning ("ANSI C does not support the strftime `%c' flag",
- *format_chars);
- if (index (flag_chars, *format_chars) != 0)
- {
- warning ("repeated `%c' flag in format",
- *format_chars);
- ++format_chars;
- }
- else
- {
- i = strlen (flag_chars);
- flag_chars[i++] = *format_chars++;
- flag_chars[i] = 0;
- }
+ has_operand_number = 1;
+ main_arg_num = opnum + info->first_arg_num - 1;
}
- while (ISDIGIT ((unsigned char) *format_chars))
+ }
+
+ /* Read any format flags, but do not yet validate them beyond removing
+ duplicates, since in general validation depends on the rest of
+ the format. */
+ while (*format_chars != 0
+ && strchr (fki->flag_chars, *format_chars) != 0)
+ {
+ if (strchr (flag_chars, *format_chars) != 0)
{
- wide = TRUE;
- ++format_chars;
+ const format_flag_spec *s = get_flag_spec (flag_specs,
+ *format_chars, NULL);
+ status_warning (status, "repeated %s in format", _(s->name));
}
- if (wide && pedantic)
- warning ("ANSI C does not support strftime format width");
- if (*format_chars == 'E' || *format_chars == 'O')
+ else
{
i = strlen (flag_chars);
- flag_chars[i++] = *format_chars++;
+ flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
- if (*format_chars == 'E' || *format_chars == 'O')
- {
- warning ("multiple E/O modifiers in format");
- while (*format_chars == 'E' || *format_chars == 'O')
- ++format_chars;
- }
}
+ ++format_chars;
}
- else if (info->format_type == printf_format_type)
- {
- /* See if we have a number followed by a dollar sign. If we do,
- it is an operand number, so set PARAMS to that operand. */
- if (*format_chars >= '0' && *format_chars <= '9')
- {
- const char *p = format_chars;
-
- while (*p >= '0' && *p++ <= '9')
- ;
-
- if (*p == '$')
- {
- int opnum = atoi (format_chars);
- params = first_fillin_param;
- format_chars = p + 1;
- has_operand_number = 1;
-
- for (i = 1; i < opnum && params != 0; i++)
- params = TREE_CHAIN (params);
-
- if (opnum == 0 || params == 0)
- {
- warning ("operand number out of range in format");
- return;
- }
- }
- }
-
- while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
- {
- if (index (flag_chars, *format_chars) != 0)
- warning ("repeated `%c' flag in format", *format_chars++);
- else
- {
- i = strlen (flag_chars);
- flag_chars[i++] = *format_chars++;
- flag_chars[i] = 0;
- }
- }
- /* "If the space and + flags both appear,
- the space flag will be ignored." */
- if (index (flag_chars, ' ') != 0
- && index (flag_chars, '+') != 0)
- warning ("use of both ` ' and `+' flags in format");
- /* "If the 0 and - flags both appear,
- the 0 flag will be ignored." */
- if (index (flag_chars, '0') != 0
- && index (flag_chars, '-') != 0)
- warning ("use of both `0' and `-' flags in format");
- if (*format_chars == '*')
+ /* Read any format width, possibly * or *m$. */
+ if (fki->width_char != 0)
+ {
+ if (fki->width_type != NULL && *format_chars == '*')
{
- wide = TRUE;
+ i = strlen (flag_chars);
+ flag_chars[i++] = fki->width_char;
+ flag_chars[i] = 0;
/* "...a field width...may be indicated by an asterisk.
In this case, an int argument supplies the field width..." */
++format_chars;
if (params == 0)
{
- tfaff ();
+ status_warning (status, "too few arguments for format");
return;
}
+ if (has_operand_number != 0)
+ {
+ int opnum;
+ opnum = maybe_read_dollar_number (status, &format_chars,
+ has_operand_number == 1,
+ first_fillin_param,
+ ¶ms, fki);
+ if (opnum == -1)
+ return;
+ else if (opnum > 0)
+ {
+ has_operand_number = 1;
+ arg_num = opnum + info->first_arg_num - 1;
+ }
+ else
+ has_operand_number = 0;
+ }
if (info->first_arg_num != 0)
{
cur_param = TREE_VALUE (params);
- params = TREE_CHAIN (params);
- ++arg_num;
- /* size_t is generally not valid here.
- It will work on most machines, because size_t and int
- have the same mode. But might as well warn anyway,
- since it will fail on other machines. */
- if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
- != integer_type_node)
- &&
- (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
- != unsigned_type_node))
- warning ("field width is not type int (arg %d)", arg_num);
+ if (has_operand_number <= 0)
+ {
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ }
+ width_wanted_type.wanted_type = *fki->width_type;
+ width_wanted_type.wanted_type_name = NULL;
+ width_wanted_type.pointer_count = 0;
+ width_wanted_type.char_lenient_flag = 0;
+ width_wanted_type.writing_in_flag = 0;
+ width_wanted_type.name = _("field width");
+ width_wanted_type.param = cur_param;
+ width_wanted_type.arg_num = arg_num;
+ width_wanted_type.next = NULL;
+ if (last_wanted_type != 0)
+ last_wanted_type->next = &width_wanted_type;
+ if (first_wanted_type == 0)
+ first_wanted_type = &width_wanted_type;
+ last_wanted_type = &width_wanted_type;
}
}
else
{
+ /* Possibly read a numeric width. If the width is zero,
+ we complain; for scanf this is bad according to the
+ standard, and for printf and strftime it cannot occur
+ because 0 is a flag. */
+ int non_zero_width_char = FALSE;
+ int found_width = FALSE;
while (ISDIGIT (*format_chars))
{
- wide = TRUE;
+ found_width = TRUE;
+ if (*format_chars != '0')
+ non_zero_width_char = TRUE;
++format_chars;
}
+ if (found_width && !non_zero_width_char)
+ status_warning (status, "zero width in %s format",
+ fki->name);
+ if (found_width)
+ {
+ i = strlen (flag_chars);
+ flag_chars[i++] = fki->width_char;
+ flag_chars[i] = 0;
+ }
}
- if (*format_chars == '.')
+ }
+
+ /* Read any format precision, possibly * or *m$. */
+ if (fki->precision_char != 0 && *format_chars == '.')
+ {
+ ++format_chars;
+ i = strlen (flag_chars);
+ flag_chars[i++] = fki->precision_char;
+ flag_chars[i] = 0;
+ if (fki->precision_type != NULL && *format_chars == '*')
{
- precise = TRUE;
- ++format_chars;
- if (*format_chars != '*' && !ISDIGIT (*format_chars))
- warning ("`.' not followed by `*' or digit in format");
/* "...a...precision...may be indicated by an asterisk.
In this case, an int argument supplies the...precision." */
- if (*format_chars == '*')
+ ++format_chars;
+ if (has_operand_number != 0)
{
- if (info->first_arg_num != 0)
+ int opnum;
+ opnum = maybe_read_dollar_number (status, &format_chars,
+ has_operand_number == 1,
+ first_fillin_param,
+ ¶ms, fki);
+ if (opnum == -1)
+ return;
+ else if (opnum > 0)
{
- ++format_chars;
- if (params == 0)
- {
- tfaff ();
- return;
- }
- cur_param = TREE_VALUE (params);
- params = TREE_CHAIN (params);
- ++arg_num;
- if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
- != integer_type_node)
- warning ("field width is not type int (arg %d)",
- arg_num);
+ has_operand_number = 1;
+ arg_num = opnum + info->first_arg_num - 1;
}
+ else
+ has_operand_number = 0;
}
- else
+ if (info->first_arg_num != 0)
{
- while (ISDIGIT (*format_chars))
- ++format_chars;
+ if (params == 0)
+ {
+ status_warning (status, "too few arguments for format");
+ return;
+ }
+ cur_param = TREE_VALUE (params);
+ if (has_operand_number <= 0)
+ {
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ }
+ precision_wanted_type.wanted_type = *fki->precision_type;
+ precision_wanted_type.wanted_type_name = NULL;
+ precision_wanted_type.pointer_count = 0;
+ precision_wanted_type.char_lenient_flag = 0;
+ precision_wanted_type.writing_in_flag = 0;
+ precision_wanted_type.name = _("field precision");
+ precision_wanted_type.param = cur_param;
+ precision_wanted_type.arg_num = arg_num;
+ precision_wanted_type.next = NULL;
+ if (last_wanted_type != 0)
+ last_wanted_type->next = &precision_wanted_type;
+ if (first_wanted_type == 0)
+ first_wanted_type = &precision_wanted_type;
+ last_wanted_type = &precision_wanted_type;
}
}
+ else
+ {
+ while (ISDIGIT (*format_chars))
+ ++format_chars;
+ }
}
- aflag = 0;
-
- if (info->format_type != strftime_format_type)
+ /* Read any length modifier, if this kind of format has them. */
+ fli = fki->length_char_specs;
+ length_chars = NULL;
+ length_chars_val = FMT_LEN_none;
+ length_chars_std = STD_C89;
+ if (fli)
{
- if (*format_chars == 'h' || *format_chars == 'l')
- length_char = *format_chars++;
- else if (*format_chars == 'q' || *format_chars == 'L')
- {
- length_char = *format_chars++;
- if (pedantic)
- warning ("ANSI C does not support the `%c' length modifier",
- length_char);
- }
- else if (*format_chars == 'Z' || *format_chars == 'z')
+ while (fli->name != 0 && fli->name[0] != *format_chars)
+ fli++;
+ if (fli->name != 0)
{
- length_char = *format_chars++;
- if (pedantic && (length_char == 'Z' || !flag_isoc99))
- warning ("ANSI C does not support the `%c' length modifier",
- length_char);
+ format_chars++;
+ if (fli->double_name != 0 && fli->name[0] == *format_chars)
+ {
+ format_chars++;
+ length_chars = fli->double_name;
+ length_chars_val = fli->double_index;
+ length_chars_std = fli->double_std;
+ }
+ else
+ {
+ length_chars = fli->name;
+ length_chars_val = fli->index;
+ length_chars_std = fli->std;
+ }
+ i = strlen (flag_chars);
+ flag_chars[i++] = fki->length_code_char;
+ flag_chars[i] = 0;
}
- else
- length_char = 0;
- if (length_char == 'l' && *format_chars == 'l')
+ if (pedantic)
{
- length_char = 'q', format_chars++;
- if (pedantic && !flag_isoc99)
- warning ("ANSI C does not support the `ll' length modifier");
+ /* Warn if the length modifier is non-standard. */
+ if (length_chars_std > C_STD_VER)
+ status_warning (status, "%s does not support the `%s' %s length modifier",
+ C_STD_NAME (length_chars_std), length_chars,
+ fki->name);
}
- else if (length_char == 'h' && *format_chars == 'h')
+ }
+
+ /* Read any modifier (strftime E/O). */
+ if (fki->modifier_chars != NULL)
+ {
+ while (*format_chars != 0
+ && strchr (fki->modifier_chars, *format_chars) != 0)
{
- length_char = 'H', format_chars++;
- if (pedantic && !flag_isoc99)
- warning ("ANSI C does not support the `hh' length modifier");
+ if (strchr (flag_chars, *format_chars) != 0)
+ {
+ const format_flag_spec *s = get_flag_spec (flag_specs,
+ *format_chars, NULL);
+ status_warning (status, "repeated %s in format", _(s->name));
+ }
+ else
+ {
+ i = strlen (flag_chars);
+ flag_chars[i++] = *format_chars;
+ flag_chars[i] = 0;
+ }
+ ++format_chars;
}
- if (*format_chars == 'a' && info->format_type == scanf_format_type)
+ }
+
+ /* Handle the scanf allocation kludge. */
+ if (fki->flags & FMT_FLAG_SCANF_A_KLUDGE)
+ {
+ if (*format_chars == 'a' && !flag_isoc99)
{
if (format_chars[1] == 's' || format_chars[1] == 'S'
|| format_chars[1] == '[')
{
/* `a' is used as a flag. */
- aflag = 1;
+ i = strlen (flag_chars);
+ flag_chars[i++] = 'a';
+ flag_chars[i] = 0;
format_chars++;
}
}
- if (suppressed && length_char != 0)
- warning ("use of `*' and `%c' together in format", length_char);
}
+
format_char = *format_chars;
if (format_char == 0
- || (info->format_type != strftime_format_type && format_char == '%'))
+ || (!(fki->flags & FMT_FLAG_FANCY_PERCENT_OK) && format_char == '%'))
{
- warning ("conversion lacks type at end of format");
+ status_warning (status, "conversion lacks type at end of format");
continue;
}
- /* The m, C, and S formats are GNU extensions. */
- if (pedantic && info->format_type != strftime_format_type
- && (format_char == 'm' || format_char == 'C' || format_char == 'S'))
- warning ("ANSI C does not support the `%c' format", format_char);
- /* The a and A formats are C99 extensions. */
- if (pedantic && info->format_type != strftime_format_type
- && (format_char == 'a' || format_char == 'A')
- && !flag_isoc99)
- warning ("ANSI C does not support the `%c' format", format_char);
format_chars++;
- switch (info->format_type)
- {
- case printf_format_type:
- fci = print_char_table;
- break;
- case scanf_format_type:
- fci = scan_char_table;
- break;
- case strftime_format_type:
- fci = time_char_table;
- break;
- default:
- abort ();
- }
+ fci = fki->conversion_specs;
while (fci->format_chars != 0
- && index (fci->format_chars, format_char) == 0)
+ && strchr (fci->format_chars, format_char) == 0)
++fci;
if (fci->format_chars == 0)
{
if (ISGRAPH(format_char))
- warning ("unknown conversion type character `%c' in format",
+ status_warning (status, "unknown conversion type character `%c' in format",
format_char);
else
- warning ("unknown conversion type character 0x%x in format",
+ status_warning (status, "unknown conversion type character 0x%x in format",
format_char);
continue;
}
if (pedantic)
{
- if (index (fci->flag_chars, 'G') != 0)
- warning ("ANSI C does not support `%%%c'", format_char);
- if (index (fci->flag_chars, 'o') != 0
- && index (flag_chars, 'O') != 0)
- warning ("ANSI C does not support `%%O%c'", format_char);
+ if (fci->std > C_STD_VER)
+ status_warning (status, "%s does not support the `%%%c' %s format",
+ C_STD_NAME (fci->std), format_char, fki->name);
+ }
+
+ /* Validate the individual flags used, removing any that are invalid. */
+ {
+ int d = 0;
+ for (i = 0; flag_chars[i] != 0; i++)
+ {
+ const format_flag_spec *s = get_flag_spec (flag_specs,
+ flag_chars[i], NULL);
+ flag_chars[i - d] = flag_chars[i];
+ if (flag_chars[i] == fki->length_code_char)
+ continue;
+ if (strchr (fci->flag_chars, flag_chars[i]) == 0)
+ {
+ status_warning (status, "%s used with `%%%c' %s format",
+ _(s->name), format_char, fki->name);
+ d++;
+ continue;
+ }
+ if (pedantic)
+ {
+ const format_flag_spec *t;
+ if (s->std > C_STD_VER)
+ status_warning (status, "%s does not support %s",
+ C_STD_NAME (s->std), _(s->long_name));
+ t = get_flag_spec (flag_specs, flag_chars[i], fci->flags2);
+ if (t != NULL && t->std > s->std)
+ {
+ const char *long_name = (t->long_name != NULL
+ ? t->long_name
+ : s->long_name);
+ if (t->std > C_STD_VER)
+ status_warning (status, "%s does not support %s with the `%%%c' %s format",
+ C_STD_NAME (t->std), _(long_name),
+ format_char, fki->name);
+ }
+ }
+ }
+ flag_chars[i - d] = 0;
+ }
+
+ if ((fki->flags & FMT_FLAG_SCANF_A_KLUDGE)
+ && strchr (flag_chars, 'a') != 0)
+ aflag = 1;
+
+ if (fki->suppression_char
+ && strchr (flag_chars, fki->suppression_char) != 0)
+ suppressed = 1;
+
+ /* Validate the pairs of flags used. */
+ for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
+ {
+ const format_flag_spec *s, *t;
+ if (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0)
+ continue;
+ if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0)
+ continue;
+ if (bad_flag_pairs[i].predicate != 0
+ && strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0)
+ continue;
+ s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL);
+ t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL);
+ if (bad_flag_pairs[i].ignored)
+ {
+ if (bad_flag_pairs[i].predicate != 0)
+ status_warning (status, "%s ignored with %s and `%%%c' %s format",
+ _(s->name), _(t->name), format_char,
+ fki->name);
+ else
+ status_warning (status, "%s ignored with %s in %s format",
+ _(s->name), _(t->name), fki->name);
+ }
+ else
+ {
+ if (bad_flag_pairs[i].predicate != 0)
+ status_warning (status, "use of %s and %s together with `%%%c' %s format",
+ _(s->name), _(t->name), format_char,
+ fki->name);
+ else
+ status_warning (status, "use of %s and %s together in %s format",
+ _(s->name), _(t->name), fki->name);
+ }
}
- if (wide && index (fci->flag_chars, 'w') == 0)
- warning ("width used with `%c' format", format_char);
- if (index (fci->flag_chars, '2') != 0)
- warning ("`%%%c' yields only last 2 digits of year", format_char);
- else if (index (fci->flag_chars, '3') != 0)
- warning ("`%%%c' yields only last 2 digits of year in some locales",
- format_char);
- if (precise && index (fci->flag_chars, 'p') == 0)
- warning ("precision used with `%c' format", format_char);
- if (aflag && index (fci->flag_chars, 'a') == 0)
+
+ /* Give Y2K warnings. */
+ if (warn_format_y2k)
{
- warning ("`a' flag used with `%c' format", format_char);
- /* To simplify the following code. */
- aflag = 0;
+ int y2k_level = 0;
+ if (strchr (fci->flags2, '4') != 0)
+ if (strchr (flag_chars, 'E') != 0)
+ y2k_level = 3;
+ else
+ y2k_level = 2;
+ else if (strchr (fci->flags2, '3') != 0)
+ y2k_level = 3;
+ else if (strchr (fci->flags2, '2') != 0)
+ y2k_level = 2;
+ if (y2k_level == 3)
+ status_warning (status, "`%%%c' yields only last 2 digits of year in some locales",
+ format_char);
+ else if (y2k_level == 2)
+ status_warning (status, "`%%%c' yields only last 2 digits of year", format_char);
}
- /* The a flag is a GNU extension. */
- else if (pedantic && aflag)
- warning ("ANSI C does not support the `a' flag");
- if (info->format_type == scanf_format_type && format_char == '[')
+
+ if (strchr (fci->flags2, '[') != 0)
{
/* Skip over scan set, in case it happens to have '%' in it. */
if (*format_chars == '^')
++format_chars;
if (*format_chars != ']')
/* The end of the format string was reached. */
- warning ("no closing `]' for `%%[' format");
+ status_warning (status, "no closing `]' for `%%[' format");
}
- if (suppressed)
+
+ wanted_type = 0;
+ wanted_type_name = 0;
+ if (fki->flags & FMT_FLAG_ARG_CONVERT)
{
- if (index (fci->flag_chars, '*') == 0)
- warning ("suppression of `%c' conversion in format", format_char);
- continue;
+ wanted_type = (fci->types[length_chars_val].type
+ ? *fci->types[length_chars_val].type : 0);
+ wanted_type_name = fci->types[length_chars_val].name;
+ wanted_type_std = fci->types[length_chars_val].std;
+ if (wanted_type == 0)
+ {
+ status_warning (status, "use of `%s' length modifier with `%c' type character",
+ length_chars, format_char);
+ /* Heuristic: skip one argument when an invalid length/type
+ combination is encountered. */
+ arg_num++;
+ if (params == 0)
+ {
+ status_warning (status, "too few arguments for format");
+ return;
+ }
+ params = TREE_CHAIN (params);
+ continue;
+ }
+ else if (pedantic
+ /* Warn if non-standard, provided it is more non-standard
+ than the length and type characters that may already
+ have been warned for. */
+ && wanted_type_std > length_chars_std
+ && wanted_type_std > fci->std)
+ {
+ if (wanted_type_std > C_STD_VER)
+ status_warning (status, "%s does not support the `%%%s%c' %s format",
+ C_STD_NAME (wanted_type_std), length_chars,
+ format_char, fki->name);
+ }
}
- for (i = 0; flag_chars[i] != 0; ++i)
+
+ /* Finally. . .check type of argument against desired type! */
+ if (info->first_arg_num == 0)
+ continue;
+ if ((fci->pointer_count == 0 && wanted_type == void_type_node)
+ || suppressed)
{
- if (index (fci->flag_chars, flag_chars[i]) == 0)
- warning ("flag `%c' used with type `%c'",
- flag_chars[i], format_char);
+ if (main_arg_num != 0)
+ {
+ if (suppressed)
+ status_warning (status, "operand number specified with suppressed assignment");
+ else
+ status_warning (status, "operand number specified for format taking no argument");
+ }
}
- if (info->format_type == strftime_format_type)
- continue;
- if (precise && index (flag_chars, '0') != 0
- && (format_char == 'd' || format_char == 'i'
- || format_char == 'o' || format_char == 'u'
- || format_char == 'x' || format_char == 'X'))
- warning ("`0' flag ignored with precision specifier and `%c' format",
- format_char);
- switch (length_char)
+ else
{
- default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
- case 'H': wanted_type = fci->hhlen ? *(fci->hhlen) : 0; break;
- case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
- case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
- case 'q': wanted_type = fci->qlen ? *(fci->qlen) : 0; break;
- case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
- case 'z': case 'Z': wanted_type = fci->zlen ? *fci->zlen : 0; break;
+ if (main_arg_num != 0)
+ {
+ arg_num = main_arg_num;
+ params = main_arg_params;
+ }
+ else
+ {
+ ++arg_num;
+ if (has_operand_number > 0)
+ {
+ status_warning (status, "missing $ operand number in format");
+ return;
+ }
+ else
+ has_operand_number = 0;
+ if (params == 0)
+ {
+ status_warning (status, "too few arguments for format");
+ return;
+ }
+ }
+ cur_param = TREE_VALUE (params);
+ params = TREE_CHAIN (params);
+ main_wanted_type.wanted_type = wanted_type;
+ main_wanted_type.wanted_type_name = wanted_type_name;
+ main_wanted_type.pointer_count = fci->pointer_count + aflag;
+ main_wanted_type.char_lenient_flag = 0;
+ if (strchr (fci->flags2, 'c') != 0)
+ main_wanted_type.char_lenient_flag = 1;
+ main_wanted_type.writing_in_flag = 0;
+ if (strchr (fci->flags2, 'W') != 0)
+ main_wanted_type.writing_in_flag = 1;
+ main_wanted_type.name = NULL;
+ main_wanted_type.param = cur_param;
+ main_wanted_type.arg_num = arg_num;
+ main_wanted_type.next = NULL;
+ if (last_wanted_type != 0)
+ last_wanted_type->next = &main_wanted_type;
+ if (first_wanted_type == 0)
+ first_wanted_type = &main_wanted_type;
+ last_wanted_type = &main_wanted_type;
}
+
+ if (first_wanted_type != 0)
+ check_format_types (status, first_wanted_type);
+
+ }
+}
+
+
+/* Check the argument types from a single format conversion (possibly
+ including width and precision arguments). */
+static void
+check_format_types (status, types)
+ int *status;
+ format_wanted_type *types;
+{
+ for (; types != 0; types = types->next)
+ {
+ tree cur_param;
+ tree cur_type;
+ tree orig_cur_type;
+ tree wanted_type;
+ tree promoted_type;
+ int arg_num;
+ int i;
+ int char_type_flag;
+ cur_param = types->param;
+ cur_type = TREE_TYPE (cur_param);
+ if (cur_type == error_mark_node)
+ continue;
+ char_type_flag = 0;
+ wanted_type = types->wanted_type;
+ arg_num = types->arg_num;
+
+ /* The following should not occur here. */
if (wanted_type == 0)
- warning ("use of `%c' length character with `%c' type character",
- length_char, format_char);
+ abort ();
+ if (wanted_type == void_type_node && types->pointer_count == 0)
+ abort ();
- /* Finally. . .check type of argument against desired type! */
- if (info->first_arg_num == 0)
- continue;
- if (fci->pointer_count == 0 && wanted_type == void_type_node)
- /* This specifier takes no argument. */
- continue;
- if (params == 0)
+ if (types->pointer_count == 0)
{
- tfaff ();
- return;
+ promoted_type = simple_type_promotes_to (wanted_type);
+ if (promoted_type != NULL_TREE)
+ wanted_type = promoted_type;
}
- cur_param = TREE_VALUE (params);
- params = TREE_CHAIN (params);
- ++arg_num;
- cur_type = TREE_TYPE (cur_param);
STRIP_NOPS (cur_param);
/* Check the types of any additional pointer arguments
that precede the "real" argument. */
- for (i = 0; i < fci->pointer_count + aflag; ++i)
+ for (i = 0; i < types->pointer_count; ++i)
{
if (TREE_CODE (cur_type) == POINTER_TYPE)
{
cur_type = TREE_TYPE (cur_type);
+ if (cur_type == error_mark_node)
+ break;
+
+ /* Check for writing through a NULL pointer. */
+ if (types->writing_in_flag
+ && i == 0
+ && cur_param != 0
+ && integer_zerop (cur_param))
+ status_warning (status,
+ "writing through null pointer (arg %d)",
+ arg_num);
if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR)
cur_param = TREE_OPERAND (cur_param, 0);
else
cur_param = 0;
- continue;
+ /* See if this is an attempt to write into a const type with
+ scanf or with printf "%n". Note: the writing in happens
+ at the first indirection only, if for example
+ void * const * is passed to scanf %p; passing
+ const void ** is simply passing an incompatible type. */
+ if (types->writing_in_flag
+ && i == 0
+ && (TYPE_READONLY (cur_type)
+ || (cur_param != 0
+ && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
+ || (DECL_P (cur_param)
+ && TREE_READONLY (cur_param))))))
+ status_warning (status, "writing into constant object (arg %d)", arg_num);
+
+ /* If there are extra type qualifiers beyond the first
+ indirection, then this makes the types technically
+ incompatible. */
+ if (i > 0
+ && pedantic
+ && (TYPE_READONLY (cur_type)
+ || TYPE_VOLATILE (cur_type)
+ || TYPE_RESTRICT (cur_type)))
+ status_warning (status, "extra type qualifiers in format argument (arg %d)",
+ arg_num);
+
}
- if (TREE_CODE (cur_type) != ERROR_MARK)
+ else
{
- if (fci->pointer_count + aflag == 1)
- warning ("format argument is not a pointer (arg %d)", arg_num);
+ if (types->pointer_count == 1)
+ status_warning (status, "format argument is not a pointer (arg %d)", arg_num);
else
- warning ("format argument is not a pointer to a pointer (arg %d)", arg_num);
+ status_warning (status, "format argument is not a pointer to a pointer (arg %d)", arg_num);
+ break;
}
- break;
}
- /* See if this is an attempt to write into a const type with
- scanf or with printf "%n". */
- if ((info->format_type == scanf_format_type
- || (info->format_type == printf_format_type
- && format_char == 'n'))
- && i == fci->pointer_count + aflag
- && wanted_type != 0
- && TREE_CODE (cur_type) != ERROR_MARK
- && (TYPE_READONLY (cur_type)
- || (cur_param != 0
- && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
- || (DECL_P (cur_param) && TREE_READONLY (cur_param))))))
- warning ("writing into constant object (arg %d)", arg_num);
+ if (i < types->pointer_count)
+ continue;
+
+ orig_cur_type = cur_type;
+ cur_type = TYPE_MAIN_VARIANT (cur_type);
+
+ /* Check whether the argument type is a character type. This leniency
+ only applies to certain formats, flagged with 'c'.
+ */
+ if (types->char_lenient_flag)
+ char_type_flag = (cur_type == char_type_node
+ || cur_type == signed_char_type_node
+ || cur_type == unsigned_char_type_node);
/* Check the type of the "real" argument, if there's a type we want. */
- if (i == fci->pointer_count + aflag && wanted_type != 0
- && TREE_CODE (cur_type) != ERROR_MARK
- && wanted_type != TYPE_MAIN_VARIANT (cur_type)
- /* If we want `void *', allow any pointer type.
- (Anything else would already have got a warning.) */
- && ! (wanted_type == void_type_node
- && fci->pointer_count > 0)
- /* Don't warn about differences merely in signedness. */
- && !(TREE_CODE (wanted_type) == INTEGER_TYPE
- && TREE_CODE (TYPE_MAIN_VARIANT (cur_type)) == INTEGER_TYPE
- && (TREE_UNSIGNED (wanted_type)
- ? wanted_type == (cur_type = unsigned_type (cur_type))
- : wanted_type == (cur_type = signed_type (cur_type))))
- /* Likewise, "signed char", "unsigned char" and "char" are
- equivalent but the above test won't consider them equivalent. */
- && ! (wanted_type == char_type_node
- && (TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node
- || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node)))
- {
- register const char *this;
- register const char *that;
-
- this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
- that = 0;
- if (TREE_CODE (cur_type) != ERROR_MARK
- && TYPE_NAME (cur_type) != 0
- && TREE_CODE (cur_type) != INTEGER_TYPE
- && !(TREE_CODE (cur_type) == POINTER_TYPE
- && TREE_CODE (TREE_TYPE (cur_type)) == INTEGER_TYPE))
- {
- if (TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL
- && DECL_NAME (TYPE_NAME (cur_type)) != 0)
- that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
- else
- that = IDENTIFIER_POINTER (TYPE_NAME (cur_type));
- }
+ if (wanted_type == cur_type)
+ continue;
+ /* If we want `void *', allow any pointer type.
+ (Anything else would already have got a warning.)
+ With -pedantic, only allow pointers to void and to character
+ types. */
+ if (wanted_type == void_type_node
+ && (!pedantic || (i == 1 && char_type_flag)))
+ continue;
+ /* Don't warn about differences merely in signedness, unless
+ -pedantic. With -pedantic, warn if the type is a pointer
+ target and not a character type, and for character types at
+ a second level of indirection. */
+ if (TREE_CODE (wanted_type) == INTEGER_TYPE
+ && TREE_CODE (cur_type) == INTEGER_TYPE
+ && (! pedantic || i == 0 || (i == 1 && char_type_flag))
+ && (TREE_UNSIGNED (wanted_type)
+ ? wanted_type == unsigned_type (cur_type)
+ : wanted_type == signed_type (cur_type)))
+ continue;
+ /* Likewise, "signed char", "unsigned char" and "char" are
+ equivalent but the above test won't consider them equivalent. */
+ if (wanted_type == char_type_node
+ && (! pedantic || i < 2)
+ && char_type_flag)
+ continue;
+ /* Now we have a type mismatch. */
+ {
+ register const char *this;
+ register const char *that;
+
+ this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
+ that = 0;
+ if (TYPE_NAME (orig_cur_type) != 0
+ && TREE_CODE (orig_cur_type) != INTEGER_TYPE
+ && !(TREE_CODE (orig_cur_type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (orig_cur_type)) == INTEGER_TYPE))
+ {
+ if (TREE_CODE (TYPE_NAME (orig_cur_type)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (orig_cur_type)) != 0)
+ that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (orig_cur_type)));
+ else
+ that = IDENTIFIER_POINTER (TYPE_NAME (orig_cur_type));
+ }
- /* A nameless type can't possibly match what the format wants.
- So there will be a warning for it.
- Make up a string to describe vaguely what it is. */
- if (that == 0)
- {
- if (TREE_CODE (cur_type) == POINTER_TYPE)
- that = "pointer";
- else
- that = "different type";
- }
+ /* A nameless type can't possibly match what the format wants.
+ So there will be a warning for it.
+ Make up a string to describe vaguely what it is. */
+ if (that == 0)
+ {
+ if (TREE_CODE (orig_cur_type) == POINTER_TYPE)
+ that = "pointer";
+ else
+ that = "different type";
+ }
- /* Make the warning better in case of mismatch of int vs long. */
- if (TREE_CODE (cur_type) == INTEGER_TYPE
- && TREE_CODE (wanted_type) == INTEGER_TYPE
- && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)
- && TYPE_NAME (cur_type) != 0
- && TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL)
- that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
+ /* Make the warning better in case of mismatch of int vs long. */
+ if (TREE_CODE (orig_cur_type) == INTEGER_TYPE
+ && TREE_CODE (wanted_type) == INTEGER_TYPE
+ && TYPE_PRECISION (orig_cur_type) == TYPE_PRECISION (wanted_type)
+ && TYPE_NAME (orig_cur_type) != 0
+ && TREE_CODE (TYPE_NAME (orig_cur_type)) == TYPE_DECL)
+ that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (orig_cur_type)));
- if (strcmp (this, that) != 0)
- warning ("%s format, %s arg (arg %d)", this, that, arg_num);
- }
+ if (strcmp (this, that) != 0)
+ {
+ /* There may be a better name for the format, e.g. size_t,
+ but we should allow for programs with a perverse typedef
+ making size_t something other than what the compiler
+ thinks. */
+ if (types->wanted_type_name != 0
+ && strcmp (types->wanted_type_name, that) != 0)
+ this = types->wanted_type_name;
+ if (types->name != 0)
+ status_warning (status, "%s is not type %s (arg %d)", types->name, this,
+ arg_num);
+ else
+ status_warning (status, "%s format, %s arg (arg %d)", this, that, arg_num);
+ }
+ }
}
}
+
+/* Set format warning options according to a -Wformat=n option. */
+
+void
+set_Wformat (setting)
+ int setting;
+{
+ warn_format = setting;
+ warn_format_y2k = setting;
+ warn_format_extra_args = setting;
+ if (setting != 1)
+ warn_format_nonliteral = setting;
+}
\f
/* Print a warning if a constant expression had overflow in folding.
Invoke this function on every expression that the language
return t;
}
\f
+/* A node in a list that describes references to variables (EXPR), which are
+ either read accesses if WRITER is zero, or write accesses, in which case
+ WRITER is the parent of EXPR. */
+struct tlist
+{
+ struct tlist *next;
+ tree expr, writer;
+};
+
+/* Used to implement a cache the results of a call to verify_tree. We only
+ use this for SAVE_EXPRs. */
+struct tlist_cache
+{
+ struct tlist_cache *next;
+ struct tlist *cache_before_sp;
+ struct tlist *cache_after_sp;
+ tree expr;
+};
+
+/* Obstack to use when allocating tlist structures, and corresponding
+ firstobj. */
+static struct obstack tlist_obstack;
+static char *tlist_firstobj = 0;
+
+/* Keep track of the identifiers we've warned about, so we can avoid duplicate
+ warnings. */
+static struct tlist *warned_ids;
+/* SAVE_EXPRs need special treatment. We process them only once and then
+ cache the results. */
+static struct tlist_cache *save_expr_cache;
+
+static void add_tlist PARAMS ((struct tlist **, struct tlist *, tree, int));
+static void merge_tlist PARAMS ((struct tlist **, struct tlist *, int));
+static void verify_tree PARAMS ((tree, struct tlist **, struct tlist **, tree));
+static int warning_candidate_p PARAMS ((tree));
+static void warn_for_collisions PARAMS ((struct tlist *));
+static void warn_for_collisions_1 PARAMS ((tree, tree, struct tlist *, int));
+static struct tlist *new_tlist PARAMS ((struct tlist *, tree, tree));
+static void verify_sequence_points PARAMS ((tree));
+
+/* Create a new struct tlist and fill in its fields. */
+static struct tlist *
+new_tlist (next, t, writer)
+ struct tlist *next;
+ tree t;
+ tree writer;
+{
+ struct tlist *l;
+ l = (struct tlist *) obstack_alloc (&tlist_obstack, sizeof *l);
+ l->next = next;
+ l->expr = t;
+ l->writer = writer;
+ return l;
+}
+
+/* Add duplicates of the nodes found in ADD to the list *TO. If EXCLUDE_WRITER
+ is nonnull, we ignore any node we find which has a writer equal to it. */
+
+static void
+add_tlist (to, add, exclude_writer, copy)
+ struct tlist **to;
+ struct tlist *add;
+ tree exclude_writer;
+ int copy;
+{
+ while (add)
+ {
+ struct tlist *next = add->next;
+ if (! copy)
+ add->next = *to;
+ if (! exclude_writer || add->writer != exclude_writer)
+ *to = copy ? new_tlist (*to, add->expr, add->writer) : add;
+ add = next;
+ }
+}
+
+/* Merge the nodes of ADD into TO. This merging process is done so that for
+ each variable that already exists in TO, no new node is added; however if
+ there is a write access recorded in ADD, and an occurrence on TO is only
+ a read access, then the occurrence in TO will be modified to record the
+ write. */
+
+static void
+merge_tlist (to, add, copy)
+ struct tlist **to;
+ struct tlist *add;
+ int copy;
+{
+ struct tlist **end = to;
+
+ while (*end)
+ end = &(*end)->next;
+
+ while (add)
+ {
+ int found = 0;
+ struct tlist *tmp2;
+ struct tlist *next = add->next;
+
+ for (tmp2 = *to; tmp2; tmp2 = tmp2->next)
+ if (tmp2->expr == add->expr)
+ {
+ found = 1;
+ if (! tmp2->writer)
+ tmp2->writer = add->writer;
+ }
+ if (! found)
+ {
+ *end = copy ? add : new_tlist (NULL, add->expr, add->writer);
+ end = &(*end)->next;
+ *end = 0;
+ }
+ add = next;
+ }
+}
+
+/* WRITTEN is a variable, WRITER is its parent. Warn if any of the variable
+ references in list LIST conflict with it, excluding reads if ONLY writers
+ is nonzero. */
+
+static void
+warn_for_collisions_1 (written, writer, list, only_writes)
+ tree written, writer;
+ struct tlist *list;
+ int only_writes;
+{
+ struct tlist *tmp;
+
+ /* Avoid duplicate warnings. */
+ for (tmp = warned_ids; tmp; tmp = tmp->next)
+ if (tmp->expr == written)
+ return;
+
+ while (list)
+ {
+ if (list->expr == written
+ && list->writer != writer
+ && (! only_writes || list->writer))
+ {
+ warned_ids = new_tlist (warned_ids, written, NULL_TREE);
+ warning ("operation on `%s' may be undefined",
+ IDENTIFIER_POINTER (DECL_NAME (list->expr)));
+ }
+ list = list->next;
+ }
+}
+
+/* Given a list LIST of references to variables, find whether any of these
+ can cause conflicts due to missing sequence points. */
+
+static void
+warn_for_collisions (list)
+ struct tlist *list;
+{
+ struct tlist *tmp;
+
+ for (tmp = list; tmp; tmp = tmp->next)
+ {
+ if (tmp->writer)
+ warn_for_collisions_1 (tmp->expr, tmp->writer, list, 0);
+ }
+}
+
+/* Return nonzero if X is a tree that can be verified by the sequence poitn
+ warnings. */
+static int
+warning_candidate_p (x)
+ tree x;
+{
+ return TREE_CODE (x) == VAR_DECL || TREE_CODE (x) == PARM_DECL;
+}
+
+/* Walk the tree X, and record accesses to variables. If X is written by the
+ parent tree, WRITER is the parent.
+ We store accesses in one of the two lists: PBEFORE_SP, and PNO_SP. If this
+ expression or its only operand forces a sequence point, then everything up
+ to the sequence point is stored in PBEFORE_SP. Everything else gets stored
+ in PNO_SP.
+ Once we return, we will have emitted warnings if any subexpression before
+ such a sequence point could be undefined. On a higher level, however, the
+ sequence point may not be relevant, and we'll merge the two lists.
+
+ Example: (b++, a) + b;
+ The call that processes the COMPOUND_EXPR will store the increment of B
+ in PBEFORE_SP, and the use of A in PNO_SP. The higher-level call that
+ processes the PLUS_EXPR will need to merge the two lists so that
+ eventually, all accesses end up on the same list (and we'll warn about the
+ unordered subexpressions b++ and b.
+
+ A note on merging. If we modify the former example so that our expression
+ becomes
+ (b++, b) + a
+ care must be taken not simply to add all three expressions into the final
+ PNO_SP list. The function merge_tlist takes care of that by merging the
+ before-SP list of the COMPOUND_EXPR into its after-SP list in a special
+ way, so that no more than one access to B is recorded. */
+
+static void
+verify_tree (x, pbefore_sp, pno_sp, writer)
+ tree x;
+ struct tlist **pbefore_sp, **pno_sp;
+ tree writer;
+{
+ struct tlist *tmp_before, *tmp_nosp, *tmp_list2, *tmp_list3;
+ enum tree_code code;
+ char class;
+
+ restart:
+ code = TREE_CODE (x);
+ class = TREE_CODE_CLASS (code);
+
+ if (warning_candidate_p (x))
+ {
+ *pno_sp = new_tlist (*pno_sp, x, writer);
+ return;
+ }
+
+ switch (code)
+ {
+ case CONSTRUCTOR:
+ return;
+
+ case COMPOUND_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ tmp_before = tmp_nosp = tmp_list3 = 0;
+ verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE);
+ warn_for_collisions (tmp_nosp);
+ merge_tlist (pbefore_sp, tmp_before, 0);
+ merge_tlist (pbefore_sp, tmp_nosp, 0);
+ verify_tree (TREE_OPERAND (x, 1), &tmp_list3, pno_sp, NULL_TREE);
+ merge_tlist (pbefore_sp, tmp_list3, 0);
+ return;
+
+ case COND_EXPR:
+ tmp_before = tmp_list2 = 0;
+ verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_list2, NULL_TREE);
+ warn_for_collisions (tmp_list2);
+ merge_tlist (pbefore_sp, tmp_before, 0);
+ merge_tlist (pbefore_sp, tmp_list2, 1);
+
+ tmp_list3 = tmp_nosp = 0;
+ verify_tree (TREE_OPERAND (x, 1), &tmp_list3, &tmp_nosp, NULL_TREE);
+ warn_for_collisions (tmp_nosp);
+ merge_tlist (pbefore_sp, tmp_list3, 0);
+
+ tmp_list3 = tmp_list2 = 0;
+ verify_tree (TREE_OPERAND (x, 2), &tmp_list3, &tmp_list2, NULL_TREE);
+ warn_for_collisions (tmp_list2);
+ merge_tlist (pbefore_sp, tmp_list3, 0);
+ /* Rather than add both tmp_nosp and tmp_list2, we have to merge the
+ two first, to avoid warning for (a ? b++ : b++). */
+ merge_tlist (&tmp_nosp, tmp_list2, 0);
+ add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0);
+ return;
+
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ verify_tree (TREE_OPERAND (x, 0), pno_sp, pno_sp, x);
+ return;
+
+ case MODIFY_EXPR:
+ tmp_before = tmp_nosp = tmp_list3 = 0;
+ verify_tree (TREE_OPERAND (x, 1), &tmp_before, &tmp_nosp, NULL_TREE);
+ verify_tree (TREE_OPERAND (x, 0), &tmp_list3, &tmp_list3, x);
+ /* Expressions inside the LHS are not ordered wrt. the sequence points
+ in the RHS. Example:
+ *a = (a++, 2)
+ Despite the fact that the modification of "a" is in the before_sp
+ list (tmp_before), it conflicts with the use of "a" in the LHS.
+ We can handle this by adding the contents of tmp_list3
+ to those of tmp_before, and redoing the collision warnings for that
+ list. */
+ add_tlist (&tmp_before, tmp_list3, x, 1);
+ warn_for_collisions (tmp_before);
+ /* Exclude the LHS itself here; we first have to merge it into the
+ tmp_nosp list. This is done to avoid warning for "a = a"; if we
+ didn't exclude the LHS, we'd get it twice, once as a read and once
+ as a write. */
+ add_tlist (pno_sp, tmp_list3, x, 0);
+ warn_for_collisions_1 (TREE_OPERAND (x, 0), x, tmp_nosp, 1);
+
+ merge_tlist (pbefore_sp, tmp_before, 0);
+ if (warning_candidate_p (TREE_OPERAND (x, 0)))
+ merge_tlist (&tmp_nosp, new_tlist (NULL, TREE_OPERAND (x, 0), x), 0);
+ add_tlist (pno_sp, tmp_nosp, NULL_TREE, 1);
+ return;
+
+ case CALL_EXPR:
+ /* We need to warn about conflicts among arguments and conflicts between
+ args and the function address. Side effects of the function address,
+ however, are not ordered by the sequence point of the call. */
+ tmp_before = tmp_nosp = tmp_list2 = tmp_list3 = 0;
+ verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE);
+ if (TREE_OPERAND (x, 1))
+ verify_tree (TREE_OPERAND (x, 1), &tmp_list2, &tmp_list3, NULL_TREE);
+ merge_tlist (&tmp_list3, tmp_list2, 0);
+ add_tlist (&tmp_before, tmp_list3, NULL_TREE, 0);
+ add_tlist (&tmp_before, tmp_nosp, NULL_TREE, 0);
+ warn_for_collisions (tmp_before);
+ add_tlist (pbefore_sp, tmp_before, NULL_TREE, 0);
+ return;
+
+ case TREE_LIST:
+ /* Scan all the list, e.g. indices of multi dimensional array. */
+ while (x)
+ {
+ tmp_before = tmp_nosp = 0;
+ verify_tree (TREE_VALUE (x), &tmp_before, &tmp_nosp, NULL_TREE);
+ merge_tlist (&tmp_nosp, tmp_before, 0);
+ add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0);
+ x = TREE_CHAIN (x);
+ }
+ return;
+
+ case SAVE_EXPR:
+ {
+ struct tlist_cache *t;
+ for (t = save_expr_cache; t; t = t->next)
+ if (t->expr == x)
+ break;
+
+ if (! t)
+ {
+ t = (struct tlist_cache *) obstack_alloc (&tlist_obstack,
+ sizeof *t);
+ t->next = save_expr_cache;
+ t->expr = x;
+ save_expr_cache = t;
+
+ tmp_before = tmp_nosp = 0;
+ verify_tree (TREE_OPERAND (x, 0), &tmp_before, &tmp_nosp, NULL_TREE);
+ warn_for_collisions (tmp_nosp);
+
+ tmp_list3 = 0;
+ while (tmp_nosp)
+ {
+ struct tlist *t = tmp_nosp;
+ tmp_nosp = t->next;
+ merge_tlist (&tmp_list3, t, 0);
+ }
+ t->cache_before_sp = tmp_before;
+ t->cache_after_sp = tmp_list3;
+ }
+ merge_tlist (pbefore_sp, t->cache_before_sp, 1);
+ add_tlist (pno_sp, t->cache_after_sp, NULL_TREE, 1);
+ return;
+ }
+ default:
+ break;
+ }
+
+ if (class == '1')
+ {
+ if (first_rtl_op (code) == 0)
+ return;
+ x = TREE_OPERAND (x, 0);
+ writer = 0;
+ goto restart;
+ }
+
+ switch (class)
+ {
+ case 'r':
+ case '<':
+ case '2':
+ case 'b':
+ case 'e':
+ case 's':
+ case 'x':
+ {
+ int lp;
+ int max = first_rtl_op (TREE_CODE (x));
+ for (lp = 0; lp < max; lp++)
+ {
+ tmp_before = tmp_nosp = 0;
+ verify_tree (TREE_OPERAND (x, lp), &tmp_before, &tmp_nosp, NULL_TREE);
+ merge_tlist (&tmp_nosp, tmp_before, 0);
+ add_tlist (pno_sp, tmp_nosp, NULL_TREE, 0);
+ }
+ break;
+ }
+ }
+}
+
+/* Try to warn for undefined behaviour in EXPR due to missing sequence
+ points. */
+
+static void
+verify_sequence_points (expr)
+ tree expr;
+{
+ struct tlist *before_sp = 0, *after_sp = 0;
+
+ warned_ids = 0;
+ save_expr_cache = 0;
+ if (tlist_firstobj == 0)
+ {
+ gcc_obstack_init (&tlist_obstack);
+ tlist_firstobj = obstack_alloc (&tlist_obstack, 0);
+ }
+
+ verify_tree (expr, &before_sp, &after_sp, 0);
+ warn_for_collisions (after_sp);
+ obstack_free (&tlist_obstack, tlist_firstobj);
+}
+
void
c_expand_expr_stmt (expr)
tree expr;
|| TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
expr = default_conversion (expr);
+ if (warn_sequence_point)
+ verify_sequence_points (expr);
+
if (TREE_TYPE (expr) != error_mark_node
&& !COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (expr))
&& TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
error ("expression statement has incomplete type");
- expand_expr_stmt (expr);
+ last_expr_type = TREE_TYPE (expr);
+ add_stmt (build_stmt (EXPR_STMT, expr));
}
\f
/* Validate the expression after `case' and apply default promotions. */
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
STRIP_TYPE_NOPS (value);
+ /* In C++, the following is allowed:
+
+ const int i = 3;
+ switch (...) { case i: ... }
+
+ So, we try to reduce the VALUE to a constant that way. */
+ if (c_language == clk_cplusplus)
+ {
+ value = decl_constant_value (value);
+ STRIP_TYPE_NOPS (value);
+ value = fold (value);
+ }
if (TREE_CODE (value) != INTEGER_CST
&& value != error_mark_node)
if (mode == TYPE_MODE (build_pointer_type (integer_type_node)))
return build_pointer_type (integer_type_node);
+#ifdef VECTOR_MODE_SUPPORTED_P
+ if (mode == TYPE_MODE (V4SF_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
+ return V4SF_type_node;
+ if (mode == TYPE_MODE (V4SI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
+ return V4SI_type_node;
+ if (mode == TYPE_MODE (V2SI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
+ return V2SI_type_node;
+ if (mode == TYPE_MODE (V4HI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
+ return V4HI_type_node;
+ if (mode == TYPE_MODE (V8QI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
+ return V8QI_type_node;
+#endif
+
return 0;
}
are requested. However, if OP0 is a constant that is
>= 0, the signedness of the comparison isn't an issue,
so suppress the warning. */
- if (extra_warnings
+ if (extra_warnings && !in_system_header
&& ! (TREE_CODE (primop0) == INTEGER_CST
&& ! TREE_OVERFLOW (convert (signed_type (type),
primop0))))
break;
case LT_EXPR:
- if (extra_warnings
+ if (extra_warnings && !in_system_header
&& ! (TREE_CODE (primop0) == INTEGER_CST
&& ! TREE_OVERFLOW (convert (signed_type (type),
primop0))))
break;
default:
- break;
- }
-
- if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
- {
- tree tem = save_expr (expr);
- return (build_binary_op
- ((TREE_SIDE_EFFECTS (expr)
- ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
- truthvalue_conversion (build_unary_op (REALPART_EXPR, tem, 0)),
- truthvalue_conversion (build_unary_op (IMAGPART_EXPR, tem, 0)),
- 0));
- }
-
- return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
-}
-\f
-#if USE_CPPLIB
-/* Read the rest of a #-directive from input stream FINPUT.
- In normal use, the directive name and the white space after it
- have already been read, so they won't be included in the result.
- We allow for the fact that the directive line may contain
- a newline embedded within a character or string literal which forms
- a part of the directive.
-
- The value is a string in a reusable buffer. It remains valid
- only until the next time this function is called. */
-unsigned char *yy_cur, *yy_lim;
-
-#define GETC() (yy_cur < yy_lim ? *yy_cur++ : yy_get_token ())
-#define UNGETC(c) ((c) == EOF ? 0 : yy_cur--)
-
-int
-yy_get_token ()
-{
- for (;;)
- {
- parse_in.limit = parse_in.token_buffer;
- cpp_token = cpp_get_token (&parse_in);
- if (cpp_token == CPP_EOF)
- return -1;
- yy_lim = CPP_PWRITTEN (&parse_in);
- yy_cur = parse_in.token_buffer;
- if (yy_cur < yy_lim)
- return *yy_cur++;
- }
-}
-
-char *
-get_directive_line ()
-{
- static char *directive_buffer = NULL;
- static unsigned buffer_length = 0;
- register char *p;
- register char *buffer_limit;
- register int looking_for = 0;
- register int char_escaped = 0;
-
- if (buffer_length == 0)
- {
- directive_buffer = (char *)xmalloc (128);
- buffer_length = 128;
- }
-
- buffer_limit = &directive_buffer[buffer_length];
-
- for (p = directive_buffer; ; )
- {
- int c;
-
- /* Make buffer bigger if it is full. */
- if (p >= buffer_limit)
- {
- register unsigned bytes_used = (p - directive_buffer);
-
- buffer_length *= 2;
- directive_buffer
- = (char *)xrealloc (directive_buffer, buffer_length);
- p = &directive_buffer[bytes_used];
- buffer_limit = &directive_buffer[buffer_length];
- }
-
- c = GETC ();
-
- /* Discard initial whitespace. */
- if ((c == ' ' || c == '\t') && p == directive_buffer)
- continue;
-
- /* Detect the end of the directive. */
- if (c == '\n' && looking_for == 0)
- {
- UNGETC (c);
- c = '\0';
- }
-
- *p++ = c;
-
- if (c == 0)
- return directive_buffer;
-
- /* Handle string and character constant syntax. */
- if (looking_for)
- {
- if (looking_for == c && !char_escaped)
- looking_for = 0; /* Found terminator... stop looking. */
- }
- else
- if (c == '\'' || c == '"')
- looking_for = c; /* Don't stop buffering until we see another
- another one of these (or an EOF). */
-
- /* Handle backslash. */
- char_escaped = (c == '\\' && ! char_escaped);
- }
-}
-#else
-/* Read the rest of a #-directive from input stream FINPUT.
- In normal use, the directive name and the white space after it
- have already been read, so they won't be included in the result.
- We allow for the fact that the directive line may contain
- a newline embedded within a character or string literal which forms
- a part of the directive.
-
- The value is a string in a reusable buffer. It remains valid
- only until the next time this function is called.
-
- The terminating character ('\n' or EOF) is left in FINPUT for the
- caller to re-read. */
-
-char *
-get_directive_line (finput)
- register FILE *finput;
-{
- static char *directive_buffer = NULL;
- static unsigned buffer_length = 0;
- register char *p;
- register char *buffer_limit;
- register int looking_for = 0;
- register int char_escaped = 0;
-
- if (buffer_length == 0)
- {
- directive_buffer = (char *)xmalloc (128);
- buffer_length = 128;
- }
-
- buffer_limit = &directive_buffer[buffer_length];
-
- for (p = directive_buffer; ; )
- {
- int c;
-
- /* Make buffer bigger if it is full. */
- if (p >= buffer_limit)
- {
- register unsigned bytes_used = (p - directive_buffer);
-
- buffer_length *= 2;
- directive_buffer
- = (char *)xrealloc (directive_buffer, buffer_length);
- p = &directive_buffer[bytes_used];
- buffer_limit = &directive_buffer[buffer_length];
- }
-
- c = getc (finput);
-
- /* Discard initial whitespace. */
- if ((c == ' ' || c == '\t') && p == directive_buffer)
- continue;
-
- /* Detect the end of the directive. */
- if (looking_for == 0
- && (c == '\n' || c == EOF))
- {
- ungetc (c, finput);
- c = '\0';
- }
-
- *p++ = c;
-
- if (c == 0)
- return directive_buffer;
-
- /* Handle string and character constant syntax. */
- if (looking_for)
- {
- if (looking_for == c && !char_escaped)
- looking_for = 0; /* Found terminator... stop looking. */
- }
- else
- if (c == '\'' || c == '"')
- looking_for = c; /* Don't stop buffering until we see another
- one of these (or an EOF). */
+ break;
+ }
- /* Handle backslash. */
- char_escaped = (c == '\\' && ! char_escaped);
+ if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
+ {
+ tree tem = save_expr (expr);
+ return (build_binary_op
+ ((TREE_SIDE_EFFECTS (expr)
+ ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
+ truthvalue_conversion (build_unary_op (REALPART_EXPR, tem, 0)),
+ truthvalue_conversion (build_unary_op (IMAGPART_EXPR, tem, 0)),
+ 0));
}
+
+ return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
}
-#endif /* !USE_CPPLIB */
\f
/* Make a variant type in the proper way for C/C++, propagating qualifiers
down to the element type of an array. */
can dereference IPP and CIPP. So, we ignore cv-qualifiers on
the pointed-to types. This issue has been reported to the
C++ committee. */
- t1 = TYPE_MAIN_VARIANT (TREE_TYPE (t));
- t1 = ((TREE_CODE (t) == POINTER_TYPE)
- ? build_pointer_type (t1) : build_reference_type (t1));
+ t1 = build_type_no_quals (t);
if (t1 != t)
return get_alias_set (t1);
}
}
/* Build tree nodes and builtin functions common to both C and C++ language
- frontends.
- CPLUS_MODE is nonzero if we are called from the C++ frontend, we generate
- some stricter prototypes in that case.
- NO_BUILTINS and NO_NONANSI_BUILTINS contain the respective values of
- the language frontend flags flag_no_builtin and
- flag_no_nonansi_builtin. */
+ frontends. */
void
-c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins)
- int cplus_mode, no_builtins, no_nonansi_builtins;
+c_common_nodes_and_builtins ()
{
tree temp;
tree memcpy_ftype, memset_ftype, strlen_ftype;
- tree bzero_ftype, bcmp_ftype;
+ tree bzero_ftype, bcmp_ftype, puts_ftype, printf_ftype;
tree endlink, int_endlink, double_endlink, unsigned_endlink;
tree sizetype_endlink;
tree ptr_ftype, ptr_ftype_unsigned;
- tree void_ftype_any, void_ftype_int, int_ftype_any;
+ tree void_ftype_any, void_ftype_int, int_ftype_any, sizet_ftype_any;
tree double_ftype_double, double_ftype_double_double;
tree float_ftype_float, ldouble_ftype_ldouble;
tree int_ftype_cptr_cptr_sizet;
tree int_ftype_string_string, string_ftype_ptr_ptr;
+ tree string_ftype_string_int, string_ftype_string_string;
+ tree string_ftype_string_cstring_sizet, int_ftype_cstring_cstring_sizet;
tree long_ftype_long;
+ tree longlong_ftype_longlong;
+ tree intmax_ftype_intmax;
/* Either char* or void*. */
tree traditional_ptr_type_node;
/* Either const char* or const void*. */
tree va_list_ref_type_node;
tree va_list_arg_type_node;
+ string_type_node = build_pointer_type (char_type_node);
+ const_string_type_node
+ = build_pointer_type (build_type_variant (char_type_node, 1, 0));
+
+ wint_type_node =
+ TREE_TYPE (identifier_global_value (get_identifier (WINT_TYPE)));
+
+ intmax_type_node =
+ TREE_TYPE (identifier_global_value (get_identifier (INTMAX_TYPE)));
+ uintmax_type_node =
+ TREE_TYPE (identifier_global_value (get_identifier (UINTMAX_TYPE)));
+
+ default_function_type = build_function_type (integer_type_node, NULL_TREE);
+ ptrdiff_type_node
+ = TREE_TYPE (identifier_global_value (get_identifier (PTRDIFF_TYPE)));
+ unsigned_ptrdiff_type_node = unsigned_type (ptrdiff_type_node);
+
pushdecl (build_decl (TYPE_DECL, get_identifier ("__builtin_va_list"),
va_list_type_node));
/* We realloc here because sizetype could be int or unsigned. S'ok. */
ptr_ftype_sizetype = build_function_type (ptr_type_node, sizetype_endlink);
+ sizet_ftype_any = build_function_type (sizetype, NULL_TREE);
int_ftype_any = build_function_type (integer_type_node, NULL_TREE);
void_ftype_any = build_function_type (void_type_node, NULL_TREE);
void_ftype = build_function_type (void_type_node, endlink);
tree_cons (NULL_TREE, long_integer_type_node,
endlink));
+ longlong_ftype_longlong
+ = build_function_type (long_long_integer_type_node,
+ tree_cons (NULL_TREE, long_long_integer_type_node,
+ endlink));
+
+ intmax_ftype_intmax
+ = build_function_type (intmax_type_node,
+ tree_cons (NULL_TREE, intmax_type_node,
+ endlink));
+
int_ftype_cptr_cptr_sizet
= build_function_type (integer_type_node,
tree_cons (NULL_TREE, const_ptr_type_node,
sizetype,
endlink))));
+ void_zero_node = build_int_2 (0, 0);
+ TREE_TYPE (void_zero_node) = void_type_node;
+
/* Prototype for strcpy. */
string_ftype_ptr_ptr
= build_function_type (string_type_node,
const_string_type_node,
endlink)));
- traditional_len_type_node = (flag_traditional && ! cplus_mode
+ /* Prototype for strncpy. */
+ string_ftype_string_cstring_sizet
+ = build_function_type (string_type_node,
+ tree_cons (NULL_TREE, string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ sizetype_endlink)));
+
+ traditional_len_type_node = ((flag_traditional &&
+ c_language != clk_cplusplus)
? integer_type_node : sizetype);
traditional_len_endlink = tree_cons (NULL_TREE, traditional_len_type_node,
endlink);
const_string_type_node,
endlink)));
+ /* Prototype for strncmp. */
+ int_ftype_cstring_cstring_sizet
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ sizetype_endlink)));
+
+ /* Prototype for strstr, strpbrk, etc. */
+ string_ftype_string_string
+ = build_function_type (string_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ endlink)));
+
+ /* Prototype for strchr. */
+ string_ftype_string_int
+ = build_function_type (string_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink)));
+
/* Prototype for strlen. */
strlen_ftype
= build_function_type (traditional_len_type_node,
tree_cons (NULL_TREE, const_string_type_node,
endlink));
- traditional_ptr_type_node = (flag_traditional && ! cplus_mode
+ traditional_ptr_type_node = ((flag_traditional &&
+ c_language != clk_cplusplus)
? string_type_node : ptr_type_node);
- traditional_cptr_type_node = (flag_traditional && ! cplus_mode
+ traditional_cptr_type_node = ((flag_traditional &&
+ c_language != clk_cplusplus)
? const_string_type_node : const_ptr_type_node);
/* Prototype for memcpy. */
traditional_cptr_type_node,
traditional_len_endlink)));
+ /* Prototype for puts. */
+ puts_ftype
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ endlink));
+
+ /* Prototype for printf. */
+ printf_ftype
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ NULL_TREE));
+
builtin_function ("__builtin_constant_p", default_function_type,
BUILT_IN_CONSTANT_P, BUILT_IN_NORMAL, NULL_PTR);
BUILT_IN_NORMAL, NULL_PTR);
/* Define alloca, ffs as builtins.
Declare _exit just to mark it as volatile. */
- if (! no_builtins && ! no_nonansi_builtins)
+ if (! flag_no_builtin && ! flag_no_nonansi_builtin)
{
#ifndef SMALL_STACK
temp = builtin_function ("alloca", ptr_ftype_sizetype,
/* Suppress error if redefined as a non-function. */
DECL_BUILT_IN_NONANSI (temp) = 1;
+ temp = builtin_function ("index", string_ftype_string_int,
+ BUILT_IN_INDEX, BUILT_IN_NORMAL, NULL_PTR);
+ DECL_BUILT_IN_NONANSI (temp) = 1;
+ temp = builtin_function ("rindex", string_ftype_string_int,
+ BUILT_IN_RINDEX, BUILT_IN_NORMAL, NULL_PTR);
+ DECL_BUILT_IN_NONANSI (temp) = 1;
/* The system prototypes for these functions have many
variations, so don't specify parameters to avoid conflicts.
The expand_* functions check the argument types anyway. */
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("__builtin_labs", long_ftype_long, BUILT_IN_LABS,
BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("__builtin_llabs", longlong_ftype_longlong, BUILT_IN_LLABS,
+ BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("__builtin_imaxabs", intmax_ftype_intmax, BUILT_IN_IMAXABS,
+ BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("__builtin_saveregs", ptr_ftype, BUILT_IN_SAVEREGS,
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("__builtin_classify_type", default_function_type,
BUILT_IN_BZERO, BUILT_IN_NORMAL, "bzero");
builtin_function ("__builtin_bcmp", bcmp_ftype,
BUILT_IN_BCMP, BUILT_IN_NORMAL, "bcmp");
- builtin_function ("__builtin_strcmp", int_ftype_string_string,
- BUILT_IN_STRCMP, BUILT_IN_NORMAL, "strcmp");
+ builtin_function ("__builtin_index", string_ftype_string_int,
+ BUILT_IN_INDEX, BUILT_IN_NORMAL, "index");
+ builtin_function ("__builtin_rindex", string_ftype_string_int,
+ BUILT_IN_RINDEX, BUILT_IN_NORMAL, "rindex");
+ built_in_decls[BUILT_IN_STRCMP] =
+ builtin_function ("__builtin_strcmp", int_ftype_string_string,
+ BUILT_IN_STRCMP, BUILT_IN_NORMAL, "strcmp");
+ builtin_function ("__builtin_strncmp", int_ftype_cstring_cstring_sizet,
+ BUILT_IN_STRNCMP, BUILT_IN_NORMAL, "strncmp");
+ builtin_function ("__builtin_strstr", string_ftype_string_string,
+ BUILT_IN_STRSTR, BUILT_IN_NORMAL, "strstr");
+ builtin_function ("__builtin_strpbrk", string_ftype_string_string,
+ BUILT_IN_STRPBRK, BUILT_IN_NORMAL, "strpbrk");
+ built_in_decls[BUILT_IN_STRCHR] =
+ builtin_function ("__builtin_strchr", string_ftype_string_int,
+ BUILT_IN_STRCHR, BUILT_IN_NORMAL, "strchr");
+ builtin_function ("__builtin_strrchr", string_ftype_string_int,
+ BUILT_IN_STRRCHR, BUILT_IN_NORMAL, "strrchr");
builtin_function ("__builtin_strcpy", string_ftype_ptr_ptr,
BUILT_IN_STRCPY, BUILT_IN_NORMAL, "strcpy");
+ builtin_function ("__builtin_strncpy", string_ftype_string_cstring_sizet,
+ BUILT_IN_STRNCPY, BUILT_IN_NORMAL, "strncpy");
builtin_function ("__builtin_strlen", strlen_ftype,
BUILT_IN_STRLEN, BUILT_IN_NORMAL, "strlen");
builtin_function ("__builtin_sqrtf", float_ftype_float,
BUILT_IN_COS, BUILT_IN_NORMAL, "cos");
builtin_function ("__builtin_cosl", ldouble_ftype_ldouble,
BUILT_IN_COS, BUILT_IN_NORMAL, "cosl");
-
- if (! no_builtins)
+ built_in_decls[BUILT_IN_PUTCHAR] =
+ builtin_function ("__builtin_putchar", int_ftype_int,
+ BUILT_IN_PUTCHAR, BUILT_IN_NORMAL, "putchar");
+ built_in_decls[BUILT_IN_PUTS] =
+ builtin_function ("__builtin_puts", puts_ftype,
+ BUILT_IN_PUTS, BUILT_IN_NORMAL, "puts");
+ builtin_function ("__builtin_printf", printf_ftype,
+ BUILT_IN_PRINTF, BUILT_IN_FRONTEND, "printf");
+ /* We declare these without argument so that the initial declaration
+ for these identifiers is a builtin. That allows us to redeclare
+ them later with argument without worrying about the explicit
+ declarations in stdio.h being taken as the initial declaration.
+ Also, save the _DECL for these so we can use them later. */
+ built_in_decls[BUILT_IN_FWRITE] =
+ builtin_function ("__builtin_fwrite", sizet_ftype_any,
+ BUILT_IN_FWRITE, BUILT_IN_NORMAL, "fwrite");
+ built_in_decls[BUILT_IN_FPUTC] =
+ builtin_function ("__builtin_fputc", int_ftype_any,
+ BUILT_IN_FPUTC, BUILT_IN_NORMAL, "fputc");
+ built_in_decls[BUILT_IN_FPUTS] =
+ builtin_function ("__builtin_fputs", int_ftype_any,
+ BUILT_IN_FPUTS, BUILT_IN_NORMAL, "fputs");
+
+ if (! flag_no_builtin)
{
builtin_function ("abs", int_ftype_int, BUILT_IN_ABS,
BUILT_IN_NORMAL, NULL_PTR);
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("labs", long_ftype_long, BUILT_IN_LABS,
BUILT_IN_NORMAL, NULL_PTR);
+ if (flag_isoc99 || ! flag_no_nonansi_builtin)
+ {
+ builtin_function ("llabs", longlong_ftype_longlong, BUILT_IN_LLABS,
+ BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("imaxabs", intmax_ftype_intmax, BUILT_IN_IMAXABS,
+ BUILT_IN_NORMAL, NULL_PTR);
+ }
builtin_function ("memcpy", memcpy_ftype, BUILT_IN_MEMCPY,
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("memcmp", int_ftype_cptr_cptr_sizet, BUILT_IN_MEMCMP,
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("strcmp", int_ftype_string_string, BUILT_IN_STRCMP,
BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("strncmp", int_ftype_cstring_cstring_sizet,
+ BUILT_IN_STRNCMP, BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("strstr", string_ftype_string_string, BUILT_IN_STRSTR,
+ BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("strchr", string_ftype_string_int, BUILT_IN_STRCHR,
+ BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("strrchr", string_ftype_string_int, BUILT_IN_STRRCHR,
+ BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("strpbrk", string_ftype_string_string, BUILT_IN_STRPBRK,
+ BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("strcpy", string_ftype_ptr_ptr, BUILT_IN_STRCPY,
BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("strncpy", string_ftype_string_cstring_sizet,
+ BUILT_IN_STRNCPY, BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("strlen", strlen_ftype, BUILT_IN_STRLEN,
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("sqrtf", float_ftype_float, BUILT_IN_FSQRT,
BUILT_IN_NORMAL, NULL_PTR);
builtin_function ("cosl", ldouble_ftype_ldouble, BUILT_IN_COS,
BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("printf", printf_ftype, BUILT_IN_PRINTF,
+ BUILT_IN_FRONTEND, NULL_PTR);
+ /* We declare these without argument so that the initial
+ declaration for these identifiers is a builtin. That allows
+ us to redeclare them later with argument without worrying
+ about the explicit declarations in stdio.h being taken as the
+ initial declaration. */
+ builtin_function ("fputc", int_ftype_any, BUILT_IN_FPUTC,
+ BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("fputs", int_ftype_any, BUILT_IN_FPUTS,
+ BUILT_IN_NORMAL, NULL_PTR);
/* Declare these functions volatile
to avoid spurious "control drops through" warnings. */
- temp = builtin_function ("abort", cplus_mode ? void_ftype : void_ftype_any,
+ temp = builtin_function ("abort",
+ ((c_language == clk_cplusplus)
+ ? void_ftype : void_ftype_any),
0, NOT_BUILT_IN, NULL_PTR);
TREE_THIS_VOLATILE (temp) = 1;
TREE_SIDE_EFFECTS (temp) = 1;
-#if 0 /* ??? The C++ frontend used to do this. */
- /* Well, these are actually ANSI, but we can't set DECL_BUILT_IN on
- them... */
- DECL_BUILT_IN_NONANSI (temp) = 1;
-#endif
temp = builtin_function ("exit",
- cplus_mode ? void_ftype_int : void_ftype_any,
+ ((c_language == clk_cplusplus)
+ ? void_ftype_int : void_ftype_any),
0, NOT_BUILT_IN, NULL_PTR);
TREE_THIS_VOLATILE (temp) = 1;
TREE_SIDE_EFFECTS (temp) = 1;
-
-#if 0 /* ??? The C++ frontend used to do this. */
- /* Well, these are actually ANSI, but we can't set DECL_BUILT_IN on
- them... */
- DECL_BUILT_IN_NONANSI (temp) = 1;
-#endif
}
#if 0
BUILT_IN_NORMAL, NULL_PTR);
#endif
+ main_identifier_node = get_identifier ("main");
+
/* ??? Perhaps there's a better place to do this. But it is related
to __builtin_va_arg, so it isn't that off-the-wall. */
lang_type_promotes_to = simple_type_promotes_to;
return 1;
}
+/* Recursively examines the array elements of TYPE, until a non-array
+ element type is found. */
+
+tree
+strip_array_types (type)
+ tree type;
+{
+ while (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+
+ return type;
+}
+
/* Recognize certain built-in functions so we can make tree-codes
other than CALL_EXPR. We do this when it enables fold-const.c
to do something useful. */
{
case BUILT_IN_ABS:
case BUILT_IN_LABS:
+ case BUILT_IN_LLABS:
+ case BUILT_IN_IMAXABS:
case BUILT_IN_FABS:
if (coerced_params == 0)
return integer_zero_node;
return NULL_TREE;
}
+/* Returns non-zero if CODE is the code for a statement. */
+
+int
+statement_code_p (code)
+ enum tree_code code;
+{
+ switch (code)
+ {
+ case EXPR_STMT:
+ case COMPOUND_STMT:
+ case DECL_STMT:
+ case IF_STMT:
+ case FOR_STMT:
+ case WHILE_STMT:
+ case DO_STMT:
+ case RETURN_STMT:
+ case BREAK_STMT:
+ case CONTINUE_STMT:
+ case SCOPE_STMT:
+ case SWITCH_STMT:
+ case GOTO_STMT:
+ case LABEL_STMT:
+ case ASM_STMT:
+ case CASE_LABEL:
+ return 1;
+
+ default:
+ if (lang_statement_code_p)
+ return (*lang_statement_code_p) (code);
+ return 0;
+ }
+}
+
+/* Walk the statemen tree, rooted at *tp. Apply FUNC to all the
+ sub-trees of *TP in a pre-order traversal. FUNC is called with the
+ DATA and the address of each sub-tree. If FUNC returns a non-NULL
+ value, the traversal is aborted, and the value returned by FUNC is
+ returned. If FUNC sets WALK_SUBTREES to zero, then the subtrees of
+ the node being visited are not walked.
+
+ We don't need a without_duplicates variant of this one because the
+ statement tree is a tree, not a graph. */
+
+tree
+walk_stmt_tree (tp, func, data)
+ tree *tp;
+ walk_tree_fn func;
+ void *data;
+{
+ enum tree_code code;
+ int walk_subtrees;
+ tree result;
+ int i, len;
+
+#define WALK_SUBTREE(NODE) \
+ do \
+ { \
+ result = walk_stmt_tree (&(NODE), func, data); \
+ if (result) \
+ return result; \
+ } \
+ while (0)
+
+ /* Skip empty subtrees. */
+ if (!*tp)
+ return NULL_TREE;
+
+ /* Skip subtrees below non-statement nodes. */
+ if (!statement_code_p (TREE_CODE (*tp)))
+ return NULL_TREE;
+
+ /* Call the function. */
+ walk_subtrees = 1;
+ result = (*func) (tp, &walk_subtrees, data);
+
+ /* If we found something, return it. */
+ if (result)
+ return result;
+
+ /* Even if we didn't, FUNC may have decided that there was nothing
+ interesting below this point in the tree. */
+ if (!walk_subtrees)
+ return NULL_TREE;
+
+ /* FUNC may have modified the tree, recheck that we're looking at a
+ statement node. */
+ code = TREE_CODE (*tp);
+ if (!statement_code_p (code))
+ return NULL_TREE;
+
+ /* Walk over all the sub-trees of this operand. Statement nodes never
+ contain RTL, and we needn't worry about TARGET_EXPRs. */
+ len = TREE_CODE_LENGTH (code);
+
+ /* Go through the subtrees. We need to do this in forward order so
+ that the scope of a FOR_EXPR is handled properly. */
+ for (i = 0; i < len; ++i)
+ WALK_SUBTREE (TREE_OPERAND (*tp, i));
+
+ /* Finally visit the chain. This can be tail-recursion optimized if
+ we write it this way. */
+ return walk_stmt_tree (&TREE_CHAIN (*tp), func, data);
+
+#undef WALK_SUBTREE
+}
+
+/* Used to compare case labels. K1 and K2 are actually tree nodes
+ representing case labels, or NULL_TREE for a `default' label.
+ Returns -1 if K1 is ordered before K2, -1 if K1 is ordered after
+ K2, and 0 if K1 and K2 are equal. */
+
+int
+case_compare (k1, k2)
+ splay_tree_key k1;
+ splay_tree_key k2;
+{
+ /* Consider a NULL key (such as arises with a `default' label) to be
+ smaller than anything else. */
+ if (!k1)
+ return k2 ? -1 : 0;
+ else if (!k2)
+ return k1 ? 1 : 0;
+
+ return tree_int_cst_compare ((tree) k1, (tree) k2);
+}
+
+/* Process a case label for the range LOW_VALUE ... HIGH_VALUE. If
+ LOW_VALUE and HIGH_VALUE are both NULL_TREE then this case label is
+ actually a `default' label. If only HIGH_VALUE is NULL_TREE, then
+ case label was declared using the usual C/C++ syntax, rather than
+ the GNU case range extension. CASES is a tree containing all the
+ case ranges processed so far; COND is the condition for the
+ switch-statement itself. Returns the CASE_LABEL created, or
+ ERROR_MARK_NODE if no CASE_LABEL is created. */
+
+tree
+c_add_case_label (cases, cond, low_value, high_value)
+ splay_tree cases;
+ tree cond;
+ tree low_value;
+ tree high_value;
+{
+ tree type;
+ tree label;
+ tree case_label;
+ splay_tree_node node;
+
+ /* Create the LABEL_DECL itself. */
+ label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ DECL_CONTEXT (label) = current_function_decl;
+
+ /* If there was an error processing the switch condition, bail now
+ before we get more confused. */
+ if (!cond || cond == error_mark_node)
+ {
+ /* Add a label anyhow so that the back-end doesn't think that
+ the beginning of the switch is unreachable. */
+ if (!cases->root)
+ add_stmt (build_case_label (NULL_TREE, NULL_TREE, label));
+ return error_mark_node;
+ }
+
+ if ((low_value && TREE_TYPE (low_value)
+ && POINTER_TYPE_P (TREE_TYPE (low_value)))
+ || (high_value && TREE_TYPE (high_value)
+ && POINTER_TYPE_P (TREE_TYPE (high_value))))
+ error ("pointers are not permitted as case values");
+
+ /* Case ranges are a GNU extension. */
+ if (high_value && pedantic)
+ {
+ if (c_language == clk_cplusplus)
+ pedwarn ("ISO C++ forbids range expressions in switch statements");
+ else
+ pedwarn ("ISO C forbids range expressions in switch statements");
+ }
+
+ type = TREE_TYPE (cond);
+ if (low_value)
+ {
+ low_value = check_case_value (low_value);
+ low_value = convert_and_check (type, low_value);
+ }
+ if (high_value)
+ {
+ high_value = check_case_value (high_value);
+ high_value = convert_and_check (type, high_value);
+ }
+
+ /* If an error has occurred, bail out now. */
+ if (low_value == error_mark_node || high_value == error_mark_node)
+ {
+ if (!cases->root)
+ add_stmt (build_case_label (NULL_TREE, NULL_TREE, label));
+ return error_mark_node;
+ }
+
+ /* If the LOW_VALUE and HIGH_VALUE are the same, then this isn't
+ really a case range, even though it was written that way. Remove
+ the HIGH_VALUE to simplify later processing. */
+ if (tree_int_cst_equal (low_value, high_value))
+ high_value = NULL_TREE;
+ if (low_value && high_value
+ && !tree_int_cst_lt (low_value, high_value))
+ warning ("empty range specified");
+
+ /* Look up the LOW_VALUE in the table of case labels we already
+ have. */
+ node = splay_tree_lookup (cases, (splay_tree_key) low_value);
+ /* If there was not an exact match, check for overlapping ranges.
+ There's no need to do this if there's no LOW_VALUE or HIGH_VALUE;
+ that's a `default' label and the only overlap is an exact match. */
+ if (!node && (low_value || high_value))
+ {
+ splay_tree_node low_bound;
+ splay_tree_node high_bound;
+
+ /* Even though there wasn't an exact match, there might be an
+ overlap between this case range and another case range.
+ Since we've (inductively) not allowed any overlapping case
+ ranges, we simply need to find the greatest low case label
+ that is smaller that LOW_VALUE, and the smallest low case
+ label that is greater than LOW_VALUE. If there is an overlap
+ it will occur in one of these two ranges. */
+ low_bound = splay_tree_predecessor (cases,
+ (splay_tree_key) low_value);
+ high_bound = splay_tree_successor (cases,
+ (splay_tree_key) low_value);
+
+ /* Check to see if the LOW_BOUND overlaps. It is smaller than
+ the LOW_VALUE, so there is no need to check unless the
+ LOW_BOUND is in fact itself a case range. */
+ if (low_bound
+ && CASE_HIGH ((tree) low_bound->value)
+ && tree_int_cst_compare (CASE_HIGH ((tree) low_bound->value),
+ low_value) >= 0)
+ node = low_bound;
+ /* Check to see if the HIGH_BOUND overlaps. The low end of that
+ range is bigger than the low end of the current range, so we
+ are only interested if the current range is a real range, and
+ not an ordinary case label. */
+ else if (high_bound
+ && high_value
+ && (tree_int_cst_compare ((tree) high_bound->key,
+ high_value)
+ <= 0))
+ node = high_bound;
+ }
+ /* If there was an overlap, issue an error. */
+ if (node)
+ {
+ tree duplicate = CASE_LABEL_DECL ((tree) node->value);
+
+ if (high_value)
+ {
+ error ("duplicate (or overlapping) case value");
+ error_with_decl (duplicate,
+ "this is the first entry overlapping that value");
+ }
+ else if (low_value)
+ {
+ error ("duplicate case value") ;
+ error_with_decl (duplicate, "previously used here");
+ }
+ else
+ {
+ error ("multiple default labels in one switch");
+ error_with_decl (duplicate, "this is the first default label");
+ }
+ if (!cases->root)
+ add_stmt (build_case_label (NULL_TREE, NULL_TREE, label));
+ }
+
+ /* Add a CASE_LABEL to the statement-tree. */
+ case_label = add_stmt (build_case_label (low_value, high_value, label));
+ /* Register this case label in the splay tree. */
+ splay_tree_insert (cases,
+ (splay_tree_key) low_value,
+ (splay_tree_value) case_label);
+
+ return case_label;
+}
+
+/* Mark P (a stmt_tree) for GC. The use of a `void *' for the
+ parameter allows this function to be used as a GC-marking
+ function. */
+
+void
+mark_stmt_tree (p)
+ void *p;
+{
+ stmt_tree st = (stmt_tree) p;
+
+ ggc_mark_tree (st->x_last_stmt);
+ ggc_mark_tree (st->x_last_expr_type);
+}
+
+/* Mark LD for GC. */
+
+void
+c_mark_lang_decl (c)
+ struct c_lang_decl *c;
+{
+ ggc_mark_tree (c->saved_tree);
+}
+
+/* Mark F for GC. */
+
+void
+mark_c_language_function (f)
+ struct language_function *f;
+{
+ if (!f)
+ return;
+
+ mark_stmt_tree (&f->x_stmt_tree);
+ ggc_mark_tree (f->x_scope_stmt_stack);
+}
+
+/* Hook used by expand_expr to expand language-specific tree codes. */
+
+rtx
+c_expand_expr (exp, target, tmode, modifier)
+ tree exp;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+{
+ switch (TREE_CODE (exp))
+ {
+ case STMT_EXPR:
+ {
+ tree rtl_expr;
+ rtx result;
+
+ /* Since expand_expr_stmt calls free_temp_slots after every
+ expression statement, we must call push_temp_slots here.
+ Otherwise, any temporaries in use now would be considered
+ out-of-scope after the first EXPR_STMT from within the
+ STMT_EXPR. */
+ push_temp_slots ();
+ rtl_expr = expand_start_stmt_expr ();
+ expand_stmt (STMT_EXPR_STMT (exp));
+ expand_end_stmt_expr (rtl_expr);
+ result = expand_expr (rtl_expr, target, tmode, modifier);
+ pop_temp_slots ();
+ return result;
+ }
+ break;
+
+ case CALL_EXPR:
+ {
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+ && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == FUNCTION_DECL)
+ && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ && (DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == BUILT_IN_FRONTEND))
+ return c_expand_builtin (exp, target, tmode, modifier);
+ else
+ abort();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ abort ();
+ return NULL;
+}
+
+/* Hook used by safe_from_p to handle language-specific tree codes. */
+
+int
+c_safe_from_p (target, exp)
+ rtx target;
+ tree exp;
+{
+ /* We can see statements here when processing the body of a
+ statement-expression. For a declaration statement declaring a
+ variable, look at the variable's initializer. */
+ if (TREE_CODE (exp) == DECL_STMT)
+ {
+ tree decl = DECL_STMT_DECL (exp);
+
+ if (TREE_CODE (decl) == VAR_DECL
+ && DECL_INITIAL (decl)
+ && !safe_from_p (target, DECL_INITIAL (decl), /*top_p=*/0))
+ return 0;
+ }
+
+ /* For any statement, we must follow the statement-chain. */
+ if (statement_code_p (TREE_CODE (exp)) && TREE_CHAIN (exp))
+ return safe_from_p (target, TREE_CHAIN (exp), /*top_p=*/0);
+
+ /* Assume everything else is safe. */
+ return 1;
+}
+
+/* Hook used by unsafe_for_reeval to handle language-specific tree codes. */
+
+int
+c_unsafe_for_reeval (exp)
+ tree exp;
+{
+ /* Statement expressions may not be reevaluated. */
+ if (TREE_CODE (exp) == STMT_EXPR)
+ return 2;
+
+ /* Walk all other expressions. */
+ return -1;
+}
+
/* Tree code classes. */
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,
memcpy (tree_code_name + (int) LAST_AND_UNUSED_TREE_CODE,
c_tree_code_name,
(LAST_C_TREE_CODE - (int)LAST_AND_UNUSED_TREE_CODE) * sizeof (char *));
+ lang_unsafe_for_reeval = c_unsafe_for_reeval;
+}
+
+#define CALLED_AS_BUILT_IN(NODE) \
+ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
+
+static rtx
+c_expand_builtin (exp, target, tmode, modifier)
+ tree exp;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+{
+ tree type = TREE_TYPE (exp);
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ enum tree_code code = TREE_CODE (exp);
+ const int ignore = (target == const0_rtx
+ || ((code == NON_LVALUE_EXPR || code == NOP_EXPR
+ || code == CONVERT_EXPR || code == REFERENCE_EXPR
+ || code == COND_EXPR)
+ && TREE_CODE (type) == VOID_TYPE));
+
+ if (! optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ return expand_call (exp, target, ignore);
+
+ switch (fcode)
+ {
+ case BUILT_IN_PRINTF:
+ target = c_expand_builtin_printf (arglist, target, tmode,
+ modifier, ignore);
+ if (target)
+ return target;
+ break;
+
+ default: /* just do library call, if unknown builtin */
+ error ("built-in function `%s' not currently supported",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ }
+
+ /* The switch statement above can drop through to cause the function
+ to be called normally. */
+ return expand_call (exp, target, ignore);
+}
+
+/* Check an arglist to *printf for problems. The arglist should start
+ at the format specifier, with the remaining arguments immediately
+ following it. */
+static int
+is_valid_printf_arglist (arglist)
+ tree arglist;
+{
+ /* Save this value so we can restore it later. */
+ const int SAVE_pedantic = pedantic;
+ int diagnostic_occurred = 0;
+
+ /* Set this to a known value so the user setting won't affect code
+ generation. */
+ pedantic = 1;
+ /* Check to make sure there are no format specifier errors. */
+ check_function_format (&diagnostic_occurred,
+ maybe_get_identifier("printf"),
+ NULL_TREE, arglist);
+
+ /* Restore the value of `pedantic'. */
+ pedantic = SAVE_pedantic;
+
+ /* If calling `check_function_format_ptr' produces a warning, we
+ return false, otherwise we return true. */
+ return ! diagnostic_occurred;
+}
+
+/* If the arguments passed to printf are suitable for optimizations,
+ we attempt to transform the call. */
+static rtx
+c_expand_builtin_printf (arglist, target, tmode, modifier, ignore)
+ tree arglist;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+ int ignore;
+{
+ tree fn_putchar = built_in_decls[BUILT_IN_PUTCHAR],
+ fn_puts = built_in_decls[BUILT_IN_PUTS];
+ tree fn, format_arg, stripped_string;
+
+ /* If the return value is used, or the replacement _DECL isn't
+ initialized, don't do the transformation. */
+ if (!ignore || !fn_putchar || !fn_puts)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ if (arglist == 0
+ || (TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE))
+ return 0;
+
+ /* Check the specifier vs. the parameters. */
+ if (!is_valid_printf_arglist (arglist))
+ return 0;
+
+ format_arg = TREE_VALUE (arglist);
+ stripped_string = format_arg;
+ STRIP_NOPS (stripped_string);
+ if (stripped_string && TREE_CODE (stripped_string) == ADDR_EXPR)
+ stripped_string = TREE_OPERAND (stripped_string, 0);
+
+ /* If the format specifier isn't a STRING_CST, punt. */
+ if (TREE_CODE (stripped_string) != STRING_CST)
+ return 0;
+
+ /* OK! We can attempt optimization. */
+
+ /* If the format specifier was "%s\n", call __builtin_puts(arg2). */
+ if (strcmp (TREE_STRING_POINTER (stripped_string), "%s\n") == 0)
+ {
+ arglist = TREE_CHAIN (arglist);
+ fn = fn_puts;
+ }
+ /* If the format specifier was "%c", call __builtin_putchar (arg2). */
+ else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
+ {
+ arglist = TREE_CHAIN (arglist);
+ fn = fn_putchar;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+ return 0;
+
+ /* If the resulting constant string has a length of 1, call
+ putchar. Note, TREE_STRING_LENGTH includes the terminating
+ NULL in its count. */
+ if (TREE_STRING_LENGTH (stripped_string) == 2)
+ {
+ /* Given printf("c"), (where c is any one character,)
+ convert "c"[0] to an int and pass that to the replacement
+ function. */
+ arglist = build_int_2 (TREE_STRING_POINTER (stripped_string)[0], 0);
+ arglist = build_tree_list (NULL_TREE, arglist);
+
+ fn = fn_putchar;
+ }
+ /* If the resulting constant was "string\n", call
+ __builtin_puts("string"). Ensure "string" has at least one
+ character besides the trailing \n. Note, TREE_STRING_LENGTH
+ includes the terminating NULL in its count. */
+ else if (TREE_STRING_LENGTH (stripped_string) > 2
+ && TREE_STRING_POINTER (stripped_string)
+ [TREE_STRING_LENGTH (stripped_string) - 2] == '\n')
+ {
+ /* Create a NULL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ const int newlen = TREE_STRING_LENGTH (stripped_string) - 1;
+ char *newstr = (char *) alloca (newlen);
+ memcpy (newstr, TREE_STRING_POINTER (stripped_string), newlen - 1);
+ newstr[newlen - 1] = 0;
+
+ arglist = combine_strings (build_string (newlen, newstr));
+ arglist = build_tree_list (NULL_TREE, arglist);
+ fn = fn_puts;
+ }
+ else
+ /* We'd like to arrange to call fputs(string) here, but we
+ need stdout and don't have a way to get it ... yet. */
+ return 0;
+ }
+
+ return expand_expr (build_function_call (fn, arglist),
+ (ignore ? const0_rtx : target),
+ tmode, modifier);
+}
+\f
+
+/* Given a boolean expression ARG, return a tree representing an increment
+ or decrement (as indicated by CODE) of ARG. The front end must check for
+ invalid cases (e.g., decrement in C++). */
+tree
+boolean_increment (code, arg)
+ enum tree_code code;
+ tree arg;
+{
+ tree val;
+ tree true_res = (c_language == clk_cplusplus
+ ? boolean_true_node
+ : c_bool_true_node);
+ arg = stabilize_reference (arg);
+ switch (code)
+ {
+ case PREINCREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res);
+ break;
+ case POSTINCREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res);
+ arg = save_expr (arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val);
+ break;
+ case PREDECREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg));
+ break;
+ case POSTDECREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg));
+ arg = save_expr (arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val);
+ break;
+ default:
+ abort ();
+ }
+ TREE_SIDE_EFFECTS (val) = 1;
+ return val;
}