X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gcc%2Fc-common.c;h=e07a7d32f4506c1364eff0c6e0b1977caccdb189;hb=3808a17d78e26699a6a0aa8b800c8b16f488be00;hp=2f35541214af7e3dd5af269a3070a8231f92d99c;hpb=6a357625bd4bdc4129446c2df296a9808e3e9812;p=platform%2Fupstream%2Fgcc.git diff --git a/gcc/c-common.c b/gcc/c-common.c index 2f35541..e07a7d3 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -22,7 +22,6 @@ Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "tree.h" -#include "c-common.h" #include "flags.h" #include "toplev.h" #include "output.h" @@ -30,17 +29,38 @@ Boston, MA 02111-1307, USA. */ #include "rtl.h" #include "ggc.h" #include "expr.h" +#include "c-common.h" +#include "defaults.h" #include "tm_p.h" #include "intl.h" - -#if USE_CPPLIB +#include "diagnostic.h" +#include "obstack.h" #include "cpplib.h" cpp_reader parse_in; -#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. @@ -139,6 +159,40 @@ cpp_reader parse_in; 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; @@ -149,6 +203,14 @@ tree (*make_fname_decl) PARAMS ((tree, const char *, int)); 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; @@ -181,8 +243,8 @@ typedef struct int line; const char *file; int needs_warning; + tree if_stmt; } if_elt; -static void tfaff PARAMS ((void)); static if_elt *if_stack; @@ -192,15 +254,16 @@ static int if_stack_space = 0; /* 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) { @@ -213,17 +276,29 @@ c_expand_start_cond (cond, exitflag, compstmt_count) 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 @@ -234,10 +309,10 @@ c_expand_end_cond () 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 @@ -256,8 +331,15 @@ c_expand_start_else () 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__. */ @@ -330,7 +412,7 @@ combine_strings (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 @@ -369,9 +451,7 @@ combine_strings (strings) else *q = 0; - value = make_node (STRING_CST); - TREE_STRING_POINTER (value) = p; - TREE_STRING_LENGTH (value) = length; + value = build_string (length, p); } else { @@ -384,7 +464,7 @@ combine_strings (strings) /* Compute the number of elements, for the array type. */ nchars = wide_flag ? length / wchar_bytes : length; - if (pedantic && nchars > nchars_max) + 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); @@ -736,6 +816,12 @@ decl_attributes (node, attributes, prefix_attributes) 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); @@ -931,6 +1017,12 @@ decl_attributes (node, attributes, prefix_attributes) } } + 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); @@ -1220,6 +1312,46 @@ enum format_std_version 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". */ @@ -1268,18 +1400,67 @@ typedef struct 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 options. + /* List of other modifier characters allowed with these specifiers. This lists flags, and additionally "w" for width, "p" for precision, - "c" for generic character pointers being allowed, "a" for scanf - "a" allocation extension (not applicable in C99 mode), "*" for - scanf suppression, "2" for strftime two digit year formats, "3" - for strftime formats giving two digit years in some locales, "E" - and "O" for those strftime modifiers, and "o" if use of strftime "O" - is a GNU extension beyond C99. */ + "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 { @@ -1289,6 +1470,34 @@ typedef struct 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; @@ -1352,6 +1561,76 @@ static const format_length_info scanf_length_specs[] = }; +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 } @@ -1403,87 +1682,100 @@ static const format_length_info scanf_length_specs[] = #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 NULL /* intmax_t not yet implemented. */ +#define T_IM &intmax_type_node #define T99_IM { STD_C99, "intmax_t", T_IM } -#define T_UIM NULL /* uintmax_t not yet implemented. */ +#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_I, T89_I, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "-wp0 +'I" }, - { "oxX", 0, STD_C89, { T89_UI, T99_UI, T89_UI, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0#" }, - { "u", 0, STD_C89, { T89_UI, T99_UI, T89_UI, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0'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 }, "-wpc" }, - { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wc" }, - { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "" }, + { "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 +#" }, + { "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" }, + { "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 } + { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "" }, + { NULL, 0, 0, NOLENGTHS, 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" }, - { "ouxX", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "*w" }, - { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "*w" }, - { "c", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*cw" }, - { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*acw" }, - { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*acw" }, - { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w" }, - { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "" }, + { "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" }, + { "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" }, - { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw" }, - { NULL, 0, 0, NOLENGTHS, NULL } + { "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 time_char_table[] = { /* C89 conversion specifiers. */ - { "ABZab", 0, STD_C89, NOLENGTHS, "^#" }, - { "cx", 0, STD_C89, NOLENGTHS, "3E" }, - { "HIMSUWdmw", 0, STD_C89, NOLENGTHS, "-_0Ow" }, - { "j", 0, STD_C89, NOLENGTHS, "-_0Oow" }, - { "p", 0, STD_C89, NOLENGTHS, "#" }, - { "X", 0, STD_C89, NOLENGTHS, "E" }, - { "y", 0, STD_C89, NOLENGTHS, "2EO-_0w" }, - { "Y", 0, STD_C89, NOLENGTHS, "-_0EOow" }, - { "%", 0, STD_C89, NOLENGTHS, "" }, + { "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, "-_0EOow" }, - { "D", 0, STD_C99, NOLENGTHS, "2" }, - { "eVu", 0, STD_C99, NOLENGTHS, "-_0Ow" }, - { "FRTnrt", 0, STD_C99, NOLENGTHS, "" }, - { "g", 0, STD_C99, NOLENGTHS, "2Oo-_0w" }, - { "G", 0, STD_C99, NOLENGTHS, "-_0Oow" }, - { "h", 0, STD_C99, NOLENGTHS, "^#" }, - { "z", 0, STD_C99, NOLENGTHS, "Oo" }, + { "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 } + { "kls", 0, STD_EXT, NOLENGTHS, "-_0Ow", "" }, + { "P", 0, STD_EXT, NOLENGTHS, "", "" }, + { NULL, 0, 0, NOLENGTHS, 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 }, - { "scanf", scanf_length_specs, scan_char_table }, - { "strftime", NULL, time_char_table } + { "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 + } }; @@ -1509,14 +1801,57 @@ typedef struct international_format_info 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 ((const char **, int, - tree, tree *)); -static void finish_dollar_format_checking PARAMS ((void)); +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 void check_format_types PARAMS ((format_wanted_type *)); +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 ISO C functions are always checked (whether is @@ -1538,6 +1873,8 @@ init_function_format_info () /* 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, @@ -1660,21 +1997,18 @@ record_international_format (name, assembler_name, format_num) info->format_num = format_num; } - -static void -tfaff () -{ - warning ("too few arguments for format"); -} /* 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; @@ -1689,12 +2023,60 @@ check_function_format (name, assembler_name, 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; @@ -1749,11 +2131,14 @@ init_dollar_format_checking (first_arg_num, params) a $ format is found, *FORMAT is updated to point just after it. */ static int -maybe_read_dollar_number (format, dollar_needed, params, param_ptr) +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; @@ -1762,7 +2147,7 @@ maybe_read_dollar_number (format, dollar_needed, params, param_ptr) { if (dollar_needed) { - warning ("missing $ operand number in format"); + status_warning (status, "missing $ operand number in format"); return -1; } else @@ -1783,7 +2168,7 @@ maybe_read_dollar_number (format, dollar_needed, params, param_ptr) { if (dollar_needed) { - warning ("missing $ operand number in format"); + status_warning (status, "missing $ operand number in format"); return -1; } else @@ -1792,13 +2177,15 @@ maybe_read_dollar_number (format, dollar_needed, params, param_ptr) *format = fcp + 1; if (pedantic && !dollar_format_warned) { - warning ("ISO C does not support %%n$ operand number formats"); + 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)) { - warning ("operand number out of range in format"); + status_warning (status, "operand number out of range in format"); return -1; } if (argnum > dollar_max_arg_used) @@ -1814,7 +2201,16 @@ maybe_read_dollar_number (format, dollar_needed, params, param_ptr) nalloc - dollar_arguments_alloc); dollar_arguments_alloc = nalloc; } - dollar_arguments_used[argnum - 1] = 1; + 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; @@ -1844,17 +2240,57 @@ maybe_read_dollar_number (format, dollar_needed, params, param_ptr) here. */ static void -finish_dollar_format_checking () +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]) - warning ("format argument %d unused before used argument %d in $-style format", + 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) - warning ("unused arguments in $-style format"); + { + 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; } @@ -1863,38 +2299,14 @@ finish_dollar_format_checking () 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; - 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; - int format_length; tree format_tree; - tree cur_param; - tree wanted_type; - 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; - format_wanted_type *last_wanted_type; - tree first_fillin_param; - const char *format_chars; - const format_kind_info *fki = NULL; - const format_length_info *fli = NULL; - const format_char_info *fci = NULL; - char flag_chars[8]; - /* -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; - + 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) @@ -1910,9 +2322,77 @@ check_format_info (info, params) 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; + + check_format_info_recurse (status, &res, info, format_tree, params, arg_num); + + if (res.number_non_literal > 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"); + } + + /* 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 @@ -1923,12 +2403,12 @@ check_format_info (info, params) /* See if this is a call to a known internationalization function that modifies the format arg. */ - international_format_info *info; + international_format_info *iinfo; - 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))) + 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; @@ -1936,53 +2416,119 @@ check_format_info (info, params) 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) { @@ -1991,31 +2537,88 @@ check_format_info (info, params) 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; - first_fillin_param = params; init_dollar_format_checking (info->first_arg_num, first_fillin_param); - fki = &format_types[info->format_type]; + while (1) { - int aflag; - first_wanted_type = NULL; - last_wanted_type = NULL; + 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 (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) - warning ("too many arguments for format"); + { + res->number_other--; + res->number_extra_args++; + } if (has_operand_number > 0) - finish_dollar_format_checking (); + 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 == '%') @@ -2024,144 +2627,79 @@ check_format_info (info, params) 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) { - int non_zero_width_char = FALSE; - suppressed = *format_chars == '*'; - if (suppressed) - ++format_chars; - else if (has_operand_number != 0) - { - int opnum; - opnum = maybe_read_dollar_number (&format_chars, - has_operand_number == 1, - first_fillin_param, ¶ms); - 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; - } - while (ISDIGIT (*format_chars)) + /* 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) { - wide = TRUE; - if (*format_chars != '0') - non_zero_width_char = TRUE; - ++format_chars; + has_operand_number = 1; + main_arg_num = opnum + info->first_arg_num - 1; } - if (wide && !non_zero_width_char) - warning ("zero width in scanf format"); } - else if (info->format_type == strftime_format_type) - { - while (*format_chars != 0 && index ("_-0^#", *format_chars) != 0) - { - if (pedantic) - warning ("ISO 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; - } - } - 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 ("ISO 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) - { - if (has_operand_number != 0) - { - int opnum; - opnum = maybe_read_dollar_number (&format_chars, - has_operand_number == 1, - first_fillin_param, ¶ms); - 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; - } - while (*format_chars != 0 && index (" +#0-'I", *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 (index (flag_chars, '\'') && pedantic) - warning ("ISO C does not support the `'' format flag"); - if (index (flag_chars, 'I') && pedantic) - warning ("ISO C does not support the `I' format flag"); - 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) + if (has_operand_number != 0) { int opnum; - opnum = maybe_read_dollar_number (&format_chars, 1, + opnum = maybe_read_dollar_number (status, &format_chars, + has_operand_number == 1, first_fillin_param, - ¶ms); - if (opnum <= 0) + ¶ms, fki); + if (opnum == -1) return; + else if (opnum > 0) + { + has_operand_number = 1; + arg_num = opnum + info->first_arg_num - 1; + } else - arg_num = opnum + info->first_arg_num - 1; + has_operand_number = 0; } if (info->first_arg_num != 0) { @@ -2171,7 +2709,7 @@ check_format_info (info, params) params = TREE_CHAIN (params); ++arg_num; } - width_wanted_type.wanted_type = integer_type_node; + 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; @@ -2189,72 +2727,101 @@ check_format_info (info, params) } 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 (*format_chars == '.') - { - precise = 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; + } + } + } + + /* 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 == '*') + { /* "...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) { - ++format_chars; - if (has_operand_number > 0) - { - int opnum; - opnum = maybe_read_dollar_number (&format_chars, 1, - first_fillin_param, - ¶ms); - if (opnum <= 0) - return; - else - arg_num = opnum + info->first_arg_num - 1; - } - 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) { - if (params == 0) - { - tfaff (); - return; - } - cur_param = TREE_VALUE (params); - if (has_operand_number <= 0) - { - params = TREE_CHAIN (params); - ++arg_num; - } - precision_wanted_type.wanted_type = integer_type_node; - 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; + 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; - + /* 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) { while (fli->name != 0 && fli->name[0] != *format_chars) @@ -2275,99 +2842,191 @@ check_format_info (info, params) length_chars_val = fli->index; length_chars_std = fli->std; } - } - else - { - length_chars = NULL; - length_chars_val = FMT_LEN_none; - length_chars_std = STD_C89; + i = strlen (flag_chars); + flag_chars[i++] = fki->length_code_char; + flag_chars[i] = 0; } if (pedantic) { /* Warn if the length modifier is non-standard. */ - if (length_chars_std == STD_EXT) - warning ("ISO C does not support the `%s' %s length modifier", - length_chars, fki->name); - else if ((length_chars_std == STD_C99 && !flag_isoc99) - || (length_chars_std == STD_C94 && !flag_isoc94)) - warning ("ISO C89 does not support the `%s' %s length modifier", - length_chars, fki->name); + 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); + } + } + + /* Read any modifier (strftime E/O). */ + if (fki->modifier_chars != NULL) + { + while (*format_chars != 0 + && strchr (fki->modifier_chars, *format_chars) != 0) + { + 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 - && !flag_isoc99) + } + + /* 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_chars_val != FMT_LEN_none) - warning ("use of `*' and `%s' together in format", length_chars); } + 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; } format_chars++; 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 (fci->std == STD_EXT) - warning ("ISO C does not support the `%%%c' %s format", - format_char, fki->name); - else if ((fci->std == STD_C99 && !flag_isoc99) - || (fci->std == STD_C94 && !flag_isoc94)) - warning ("ISO C89 does not support the `%%%c' %s format", - format_char, fki->name); - if (index (flag_chars, 'O') != 0) + 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 (index (fci->flag_chars, 'o') != 0) - warning ("ISO C does not support `%%O%c'", format_char); - else if (!flag_isoc99 && index (fci->flag_chars, 'O') != 0) - warning ("ISO C89 does not support `%%O%c'", format_char); + 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 (!flag_isoc99 && index (flag_chars, 'E')) - warning ("ISO C89 does not support `%%E%c'", format_char); } - if (wide && index (fci->flag_chars, 'w') == 0) - warning ("width used with `%c' format", format_char); - if (index (fci->flag_chars, '3') != 0 - || (format_char == 'y' && index (flag_chars, 'E'))) - warning ("`%%%c' yields only last 2 digits of year in some locales", - format_char); - else if (index (fci->flag_chars, '2') != 0) - warning ("`%%%c' yields only last 2 digits of year", 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 ("ISO 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 == '^') @@ -2380,86 +3039,93 @@ check_format_info (info, params) ++format_chars; if (*format_chars != ']') /* The end of the format string was reached. */ - warning ("no closing `]' for `%%[' format"); - } - if (suppressed) - { - if (index (fci->flag_chars, '*') == 0) - warning ("suppression of `%c' conversion in format", format_char); - continue; - } - for (i = 0; flag_chars[i] != 0; ++i) - { - if (index (fci->flag_chars, flag_chars[i]) == 0) - warning ("flag `%c' used with type `%c'", - flag_chars[i], format_char); + status_warning (status, "no closing `]' for `%%[' format"); } - 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); - 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) + + wanted_type = 0; + wanted_type_name = 0; + if (fki->flags & FMT_FLAG_ARG_CONVERT) { - warning ("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) + 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) { - tfaff (); - return; + 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); } - 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 == STD_EXT) - warning ("ISO C does not support the `%%%s%c' %s format", - length_chars, format_char, fki->name); - else if ((wanted_type_std == STD_C99 && !flag_isoc99) - || (wanted_type_std == STD_C94 && !flag_isoc94)) - warning ("ISO C89 does not support the `%%%s%c' %s format", - length_chars, format_char, fki->name); } /* 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)) + if ((fci->pointer_count == 0 && wanted_type == void_type_node) + || suppressed) { - if (params == 0) + if (main_arg_num != 0) { - tfaff (); - return; + if (suppressed) + status_warning (status, "operand number specified with suppressed assignment"); + else + status_warning (status, "operand number specified for format taking no argument"); + } + } + else + { + 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); - ++arg_num; 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 (index (fci->flag_chars, 'c') != 0) + if (strchr (fci->flags2, 'c') != 0) main_wanted_type.char_lenient_flag = 1; main_wanted_type.writing_in_flag = 0; - if (info->format_type == scanf_format_type - || (info->format_type == printf_format_type - && format_char == 'n')) + if (strchr (fci->flags2, 'W') != 0) main_wanted_type.writing_in_flag = 1; main_wanted_type.name = NULL; main_wanted_type.param = cur_param; @@ -2473,7 +3139,7 @@ check_format_info (info, params) } if (first_wanted_type != 0) - check_format_types (first_wanted_type); + check_format_types (status, first_wanted_type); } } @@ -2482,23 +3148,41 @@ check_format_info (info, params) /* Check the argument types from a single format conversion (possibly including width and precision arguments). */ static void -check_format_types (types) +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) + abort (); + if (wanted_type == void_type_node && types->pointer_count == 0) + abort (); + + if (types->pointer_count == 0) + { + promoted_type = simple_type_promotes_to (wanted_type); + if (promoted_type != NULL_TREE) + wanted_type = promoted_type; + } + STRIP_NOPS (cur_param); /* Check the types of any additional pointer arguments @@ -2508,6 +3192,17 @@ check_format_types (types) 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); @@ -2521,132 +3216,145 @@ check_format_types (types) const void ** is simply passing an incompatible type. */ if (types->writing_in_flag && i == 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); + 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 - && TREE_CODE (cur_type) != ERROR_MARK && (TYPE_READONLY (cur_type) || TYPE_VOLATILE (cur_type) || TYPE_RESTRICT (cur_type))) - warning ("extra type qualifiers in format argument (arg %d)", + status_warning (status, "extra type qualifiers in format argument (arg %d)", arg_num); - continue; } - if (TREE_CODE (cur_type) != ERROR_MARK) + else { if (types->pointer_count == 1) - warning ("format argument is not a pointer (arg %d)", arg_num); + 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; } + 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 (TREE_CODE (cur_type) != ERROR_MARK && types->char_lenient_flag) - char_type_flag = (TYPE_MAIN_VARIANT (cur_type) == char_type_node - || TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node - || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node); + 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 == types->pointer_count && 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.) - With -pedantic, only allow pointers to void and to character - types. - */ - && ! (wanted_type == void_type_node - && types->pointer_count > 0 - && (! pedantic - || TYPE_MAIN_VARIANT (cur_type) == void_type_node - || (i == 1 && char_type_flag))) - /* 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. - */ - && !(TREE_CODE (wanted_type) == INTEGER_TYPE - && TREE_CODE (TYPE_MAIN_VARIANT (cur_type)) == INTEGER_TYPE - && (! pedantic || i == 0 || (i == 1 && char_type_flag)) - && (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 - && (! pedantic || i < 2) - && char_type_flag)) - { - 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) - { - /* 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) - warning ("%s is not type %s (arg %d)", types->name, this, - arg_num); - else - 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; +} /* Print a warning if a constant expression had overflow in folding. Invoke this function on every expression that the language @@ -2737,24 +3445,433 @@ convert_and_check (type, expr) because a conversion overflowed. */ TREE_CONSTANT_OVERFLOW (t) = TREE_CONSTANT_OVERFLOW (expr); - /* No warning for converting 0x80000000 to int. */ - if (!(TREE_UNSIGNED (type) < TREE_UNSIGNED (TREE_TYPE (expr)) - && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE - && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr)))) - /* If EXPR fits in the unsigned version of TYPE, - don't warn unless pedantic. */ - if ((pedantic - || TREE_UNSIGNED (type) - || ! int_fits_type_p (expr, unsigned_type (type))) - && skip_evaluation == 0) - warning ("overflow in implicit constant conversion"); - } - else - unsigned_conversion_warning (t, expr); + /* No warning for converting 0x80000000 to int. */ + if (!(TREE_UNSIGNED (type) < TREE_UNSIGNED (TREE_TYPE (expr)) + && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE + && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr)))) + /* If EXPR fits in the unsigned version of TYPE, + don't warn unless pedantic. */ + if ((pedantic + || TREE_UNSIGNED (type) + || ! int_fits_type_p (expr, unsigned_type (type))) + && skip_evaluation == 0) + warning ("overflow in implicit constant conversion"); + } + else + unsigned_conversion_warning (t, expr); + } + return t; +} + +/* 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); } - return t; + + 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; @@ -2765,12 +3882,16 @@ c_expand_expr_stmt (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)); } /* Validate the expression after `case' and apply default promotions. */ @@ -3637,91 +4758,6 @@ truthvalue_conversion (expr) return build_binary_op (NE_EXPR, expr, integer_zero_node, 1); } -#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. - - 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). */ - - /* Handle backslash. */ - char_escaped = (c == '\\' && ! char_escaped); - } -} -#endif /* USE_CPPLIB */ - /* Make a variant type in the proper way for C/C++, propagating qualifiers down to the element type of an array. */ @@ -3872,9 +4908,7 @@ lang_get_alias_set (t) 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); } @@ -3887,29 +4921,27 @@ lang_get_alias_set (t) } /* 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*. */ @@ -3919,6 +4951,23 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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)); @@ -3950,6 +4999,7 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) /* 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); @@ -3983,6 +5033,16 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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, @@ -4002,7 +5062,16 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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); @@ -4015,15 +5084,41 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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. */ @@ -4056,6 +5151,18 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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); @@ -4071,7 +5178,7 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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, @@ -4090,6 +5197,12 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) /* 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. */ @@ -4111,6 +5224,10 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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, @@ -4218,10 +5335,28 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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, @@ -4242,11 +5377,22 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) BUILT_IN_COS, BUILT_IN_NORMAL, "cos"); builtin_function ("__builtin_cosl", ldouble_ftype_ldouble, BUILT_IN_COS, BUILT_IN_NORMAL, "cosl"); + 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"); @@ -4254,7 +5400,7 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) builtin_function ("__builtin_fputs", int_ftype_any, BUILT_IN_FPUTS, BUILT_IN_NORMAL, "fputs"); - if (! no_builtins) + if (! flag_no_builtin) { builtin_function ("abs", int_ftype_int, BUILT_IN_ABS, BUILT_IN_NORMAL, NULL_PTR); @@ -4266,6 +5412,13 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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, @@ -4274,8 +5427,20 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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, @@ -4296,6 +5461,8 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) 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 @@ -4308,27 +5475,19 @@ c_common_nodes_and_builtins (cplus_mode, no_builtins, no_nonansi_builtins) /* 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 @@ -4421,6 +5580,19 @@ self_promoting_args_p (parms) 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. */ @@ -4442,6 +5614,8 @@ expand_tree_builtin (function, params, coerced_params) { 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; @@ -4540,6 +5714,7 @@ statement_code_p (code) case RETURN_STMT: case BREAK_STMT: case CONTINUE_STMT: + case SCOPE_STMT: case SWITCH_STMT: case GOTO_STMT: case LABEL_STMT: @@ -4627,6 +5802,314 @@ walk_stmt_tree (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, @@ -4674,4 +6157,217 @@ add_c_tree_codes () 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); +} + + +/* 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; }