/* Subroutines shared by all languages that are variants of C.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
Free Software Foundation, Inc.
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "ggc.h"
#include "expr.h"
#include "c-common.h"
-#include "tm_p.h"
-#include "intl.h"
+#include "tree-inline.h"
#include "diagnostic.h"
-
-#if USE_CPPLIB
+#include "tm_p.h"
+#include "obstack.h"
+#include "c-lex.h"
#include "cpplib.h"
-cpp_reader parse_in;
-#endif
+#include "target.h"
+cpp_reader *parse_in; /* Declared in c-lex.h. */
#undef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE TYPE_PRECISION (wchar_type_node)
+/* We let tm.h override the types used here, to handle trivial differences
+ such as the choice of unsigned int or long unsigned int for size_t.
+ When machines start needing nontrivial differences in the size type,
+ it would be best to do something here to figure out automatically
+ from other information what type to use. */
+
+#ifndef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+#endif
+
+#ifndef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+#endif
+
+#ifndef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+#endif
+
+#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 variant of the C language being processed. */
+
+enum c_language_kind c_language;
+
/* The following symbols are subsumed in the c_global_trees array, and
listed here individually for documentation purposes.
tree default_function_type;
- Function types `int (int)', etc.
-
- tree int_ftype_int;
- tree void_ftype;
- tree void_ftype_ptr;
- tree int_ftype_int;
- tree ptr_ftype_sizetype;
-
A VOID_TYPE node, packaged in a TREE_LIST.
tree void_list_node;
- The identifiers __FUNCTION__, __PRETTY_FUNCTION__, and __func__.
+ The lazily created VAR_DECLs for __FUNCTION__, __PRETTY_FUNCTION__,
+ and __func__. (C doesn't generate __FUNCTION__ and__PRETTY_FUNCTION__
+ VAR_DECLS, but C++ does.)
+
+ tree function_name_decl_node;
+ tree pretty_function_name_decl_node;
+ tree c99_function_name_decl_node;
- tree function_id_node;
- tree pretty_function_id_node;
- tree func_id_node;
+ Stack of nested function name VAR_DECLs.
+
+ tree saved_function_name_decls;
*/
int flag_no_nonansi_builtin;
-/* If non-NULL, dump the tree structure for the entire translation
- unit to this file. */
+/* Nonzero means give `double' the same size as `float'. */
+
+int flag_short_double;
-const char *flag_dump_translation_unit;
+/* Nonzero means give `wchar_t' the same size as `short'. */
+
+int flag_short_wchar;
/* Nonzero means warn about possible violations of sequence point rules. */
int warn_sequence_point;
+/* Nonzero means to warn about compile-time division by zero. */
+int warn_div_by_zero = 1;
+
/* The elements of `ridpointers' are identifier nodes for the reserved
type names and storage classes. It is indexed by a RID_... value. */
tree *ridpointers;
-tree (*make_fname_decl) PARAMS ((tree, const char *, int));
+tree (*make_fname_decl) PARAMS ((tree, int));
/* If non-NULL, the address of a language-specific function that
returns 1 for language-specific statement codes. */
This is a count, since unevaluated expressions can nest. */
int skip_evaluation;
-enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
- A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION,
- A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
- A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS, A_MALLOC,
- A_NO_LIMIT_STACK, A_PURE};
+/* Information about how a function name is generated. */
+struct fname_var_t
+{
+ tree *const decl; /* pointer to the VAR_DECL. */
+ const unsigned rid; /* RID number for the identifier. */
+ const int pretty; /* How pretty is it? */
+};
+
+/* The three ways of getting then name of the current function. */
-enum format_type { printf_format_type, scanf_format_type,
- strftime_format_type };
+const struct fname_var_t fname_vars[] =
+{
+ /* C99 compliant __func__, must be first. */
+ {&c99_function_name_decl_node, RID_C99_FUNCTION_NAME, 0},
+ /* GCC __FUNCTION__ compliant. */
+ {&function_name_decl_node, RID_FUNCTION_NAME, 0},
+ /* GCC __PRETTY_FUNCTION__ compliant. */
+ {&pretty_function_name_decl_node, RID_PRETTY_FUNCTION_NAME, 1},
+ {NULL, 0, 0},
+};
-static void add_attribute PARAMS ((enum attrs, const char *,
- int, int, int));
-static void init_attributes PARAMS ((void));
-static void record_function_format PARAMS ((tree, tree, enum format_type,
- int, int));
-static void record_international_format PARAMS ((tree, tree, int));
-static int default_valid_lang_attribute PARAMS ((tree, tree, tree, tree));
+static int constant_fits_type_p PARAMS ((tree, tree));
/* Keep a stack of if statements. We record the number of compound
statements seen up to the if keyword, as well as the line number
RECHAIN_STMTS (if_stmt, ELSE_CLAUSE (if_stmt));
}
-/* Make bindings for __FUNCTION__, __PRETTY_FUNCTION__, and __func__. */
+/* Push current bindings for the function name VAR_DECLS. */
+
+void
+start_fname_decls ()
+{
+ unsigned ix;
+ tree saved = NULL_TREE;
+
+ for (ix = 0; fname_vars[ix].decl; ix++)
+ {
+ tree decl = *fname_vars[ix].decl;
+
+ if (decl)
+ {
+ saved = tree_cons (decl, build_int_2 (ix, 0), saved);
+ *fname_vars[ix].decl = NULL_TREE;
+ }
+ }
+ if (saved || saved_function_name_decls)
+ /* Normally they'll have been NULL, so only push if we've got a
+ stack, or they are non-NULL. */
+ saved_function_name_decls = tree_cons (saved, NULL_TREE,
+ saved_function_name_decls);
+}
+
+/* Finish up the current bindings, adding them into the
+ current function's statement tree. This is done by wrapping the
+ function's body in a COMPOUND_STMT containing these decls too. This
+ must be done _before_ finish_stmt_tree is called. If there is no
+ current function, we must be at file scope and no statements are
+ involved. Pop the previous bindings. */
void
-declare_function_name ()
+finish_fname_decls ()
{
- const char *name, *printable_name;
+ unsigned ix;
+ tree body = NULL_TREE;
+ tree stack = saved_function_name_decls;
- if (current_function_decl == NULL)
+ for (; stack && TREE_VALUE (stack); stack = TREE_CHAIN (stack))
+ body = chainon (TREE_VALUE (stack), body);
+
+ if (body)
+ {
+ /* They were called into existence, so add to statement tree. */
+ body = chainon (body,
+ TREE_CHAIN (DECL_SAVED_TREE (current_function_decl)));
+ body = build_stmt (COMPOUND_STMT, body);
+
+ COMPOUND_STMT_NO_SCOPE (body) = 1;
+ TREE_CHAIN (DECL_SAVED_TREE (current_function_decl)) = body;
+ }
+
+ for (ix = 0; fname_vars[ix].decl; ix++)
+ *fname_vars[ix].decl = NULL_TREE;
+
+ if (stack)
{
- name = "";
- printable_name = "top level";
+ /* We had saved values, restore them. */
+ tree saved;
+
+ for (saved = TREE_PURPOSE (stack); saved; saved = TREE_CHAIN (saved))
+ {
+ tree decl = TREE_PURPOSE (saved);
+ unsigned ix = TREE_INT_CST_LOW (TREE_VALUE (saved));
+
+ *fname_vars[ix].decl = decl;
+ }
+ stack = TREE_CHAIN (stack);
}
+ saved_function_name_decls = stack;
+}
+
+/* Return the text name of the current function, suitable prettified
+ by PRETTY_P. */
+
+const char *
+fname_as_string (pretty_p)
+ int pretty_p;
+{
+ const char *name = NULL;
+
+ if (pretty_p)
+ name = (current_function_decl
+ ? (*decl_printable_name) (current_function_decl, 2)
+ : "top level");
+ else if (current_function_decl && DECL_NAME (current_function_decl))
+ name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
else
+ name = "";
+ return name;
+}
+
+/* Return the text name of the current function, formatted as
+ required by the supplied RID value. */
+
+const char *
+fname_string (rid)
+ unsigned rid;
+{
+ unsigned ix;
+
+ for (ix = 0; fname_vars[ix].decl; ix++)
+ if (fname_vars[ix].rid == rid)
+ break;
+ return fname_as_string (fname_vars[ix].pretty);
+}
+
+/* Return the VAR_DECL for a const char array naming the current
+ function. If the VAR_DECL has not yet been created, create it
+ now. RID indicates how it should be formatted and IDENTIFIER_NODE
+ ID is its name (unfortunately C and C++ hold the RID values of
+ keywords in different places, so we can't derive RID from ID in
+ this language independent code. */
+
+tree
+fname_decl (rid, id)
+ unsigned rid;
+ tree id;
+{
+ unsigned ix;
+ tree decl = NULL_TREE;
+
+ for (ix = 0; fname_vars[ix].decl; ix++)
+ if (fname_vars[ix].rid == rid)
+ break;
+
+ decl = *fname_vars[ix].decl;
+ if (!decl)
{
- /* Allow functions to be nameless (such as artificial ones). */
- if (DECL_NAME (current_function_decl))
- name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
- else
- name = "";
- printable_name = (*decl_printable_name) (current_function_decl, 2);
+ tree saved_last_tree = last_tree;
+
+ decl = (*make_fname_decl) (id, fname_vars[ix].pretty);
+ if (last_tree != saved_last_tree)
+ {
+ /* We created some statement tree for the decl. This belongs
+ at the start of the function, so remove it now and reinsert
+ it after the function is complete. */
+ tree stmts = TREE_CHAIN (saved_last_tree);
- /* ISO C99 defines __func__, which is a variable, not a string
- constant, and which is not a defined symbol at file scope. */
- (*make_fname_decl) (func_id_node, name, 0);
+ TREE_CHAIN (saved_last_tree) = NULL_TREE;
+ last_tree = saved_last_tree;
+ saved_function_name_decls = tree_cons (decl, stmts,
+ saved_function_name_decls);
+ }
+ *fname_vars[ix].decl = decl;
}
+ if (!ix && !current_function_decl)
+ pedwarn_with_decl (decl, "`%s' is not defined outside of function scope");
- (*make_fname_decl) (function_id_node, name, 0);
- (*make_fname_decl) (pretty_function_id_node, printable_name, 1);
+ return decl;
}
/* Given a chain of STRING_CST nodes,
combine_strings (strings)
tree strings;
{
- register tree value, t;
- register int length = 1;
+ tree value, t;
+ int length = 1;
int wide_length = 0;
int wide_flag = 0;
int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT;
if (TREE_CHAIN (strings))
{
/* More than one in the chain, so concatenate. */
- register char *p, *q;
+ char *p, *q;
/* Don't include the \0 at the end of each substring,
except for the last one.
wide_flag = 1;
}
else
- length += (TREE_STRING_LENGTH (t) - 1);
+ {
+ length += (TREE_STRING_LENGTH (t) - 1);
+ if (C_ARTIFICIAL_STRING_P (t) && !in_system_header)
+ warning ("concatenation of string literals with __FUNCTION__ is deprecated. This feature will be removed in future");
+ }
}
/* If anything is wide, the non-wides will be converted,
if (wide_flag)
length = length * wchar_bytes + wide_length;
- p = ggc_alloc_string (NULL, length);
+ p = alloca (length);
/* Copy the individual strings into the new combined string.
If the combined string is wide, convert the chars to ints
}
else
{
- int i;
+ int i, j;
for (i = 0; i < len; i++)
{
- if (WCHAR_TYPE_SIZE == HOST_BITS_PER_SHORT)
- ((short *) q)[i] = TREE_STRING_POINTER (t)[i];
+ if (BYTES_BIG_ENDIAN)
+ {
+ for (j=0; j<(WCHAR_TYPE_SIZE / BITS_PER_UNIT)-1; j++)
+ *q++ = 0;
+ *q++ = TREE_STRING_POINTER (t)[i];
+ }
else
- ((int *) q)[i] = TREE_STRING_POINTER (t)[i];
+ {
+ *q++ = TREE_STRING_POINTER (t)[i];
+ for (j=0; j<(WCHAR_TYPE_SIZE / BITS_PER_UNIT)-1; j++)
+ *q++ = 0;
+ }
}
- q += len * wchar_bytes;
}
}
if (wide_flag)
else
*q = 0;
- value = make_node (STRING_CST);
- TREE_STRING_POINTER (value) = p;
- TREE_STRING_LENGTH (value) = length;
+ value = build_string (length, p);
}
else
{
/* Compute the number of elements, for the array type. */
nchars = wide_flag ? length / wchar_bytes : length;
- if (pedantic && nchars > nchars_max && c_language == clk_c)
- pedwarn ("string length `%d' is greater than the minimum length `%d' ISO C%d is required to support",
- nchars, nchars_max, flag_isoc99 ? 99 : 89);
+ if (pedantic && nchars - 1 > nchars_max && c_language == clk_c)
+ pedwarn ("string length `%d' is greater than the length `%d' ISO C%d compilers are required to support",
+ nchars - 1, nchars_max, flag_isoc99 ? 99 : 89);
/* Create the array type for the string constant.
-Wwrite-strings says make the string constant an array of const char
return value;
}
\f
-/* To speed up processing of attributes, we maintain an array of
- IDENTIFIER_NODES and the corresponding attribute types. */
-
-/* Array to hold attribute information. */
-
-static struct {enum attrs id; tree name; int min, max, decl_req;} attrtab[50];
-
-static int attrtab_idx = 0;
-
-/* Add an entry to the attribute table above. */
-
-static void
-add_attribute (id, string, min_len, max_len, decl_req)
- enum attrs id;
- const char *string;
- int min_len, max_len;
- int decl_req;
-{
- char buf[100];
-
- attrtab[attrtab_idx].id = id;
- attrtab[attrtab_idx].name = get_identifier (string);
- attrtab[attrtab_idx].min = min_len;
- attrtab[attrtab_idx].max = max_len;
- attrtab[attrtab_idx++].decl_req = decl_req;
-
- sprintf (buf, "__%s__", string);
-
- attrtab[attrtab_idx].id = id;
- attrtab[attrtab_idx].name = get_identifier (buf);
- attrtab[attrtab_idx].min = min_len;
- attrtab[attrtab_idx].max = max_len;
- attrtab[attrtab_idx++].decl_req = decl_req;
-}
-
-/* Initialize attribute table. */
-
-static void
-init_attributes ()
-{
- add_attribute (A_PACKED, "packed", 0, 0, 0);
- add_attribute (A_NOCOMMON, "nocommon", 0, 0, 1);
- add_attribute (A_COMMON, "common", 0, 0, 1);
- add_attribute (A_NORETURN, "noreturn", 0, 0, 1);
- add_attribute (A_NORETURN, "volatile", 0, 0, 1);
- add_attribute (A_UNUSED, "unused", 0, 0, 0);
- add_attribute (A_CONST, "const", 0, 0, 1);
- add_attribute (A_T_UNION, "transparent_union", 0, 0, 0);
- add_attribute (A_CONSTRUCTOR, "constructor", 0, 0, 1);
- add_attribute (A_DESTRUCTOR, "destructor", 0, 0, 1);
- add_attribute (A_MODE, "mode", 1, 1, 1);
- add_attribute (A_SECTION, "section", 1, 1, 1);
- add_attribute (A_ALIGNED, "aligned", 0, 1, 0);
- add_attribute (A_FORMAT, "format", 3, 3, 1);
- add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1);
- add_attribute (A_WEAK, "weak", 0, 0, 1);
- add_attribute (A_ALIAS, "alias", 1, 1, 1);
- add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1);
- add_attribute (A_NO_CHECK_MEMORY_USAGE, "no_check_memory_usage", 0, 0, 1);
- add_attribute (A_MALLOC, "malloc", 0, 0, 1);
- add_attribute (A_NO_LIMIT_STACK, "no_stack_limit", 0, 0, 1);
- add_attribute (A_PURE, "pure", 0, 0, 1);
-}
+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));
+static rtx c_expand_builtin_fprintf PARAMS ((tree, rtx, enum machine_mode,
+ enum expand_modifier, int));
\f
-/* Default implementation of valid_lang_attribute, below. By default, there
- are no language-specific attributes. */
+/* Print a warning if a constant expression had overflow in folding.
+ Invoke this function on every expression that the language
+ requires to be a constant expression.
+ Note the ANSI C standard says it is erroneous for a
+ constant expression to overflow. */
-static int
-default_valid_lang_attribute (attr_name, attr_args, decl, type)
- tree attr_name ATTRIBUTE_UNUSED;
- tree attr_args ATTRIBUTE_UNUSED;
- tree decl ATTRIBUTE_UNUSED;
- tree type ATTRIBUTE_UNUSED;
+void
+constant_expression_warning (value)
+ tree value;
{
- return 0;
+ if ((TREE_CODE (value) == INTEGER_CST || TREE_CODE (value) == REAL_CST
+ || TREE_CODE (value) == COMPLEX_CST)
+ && TREE_CONSTANT_OVERFLOW (value) && pedantic)
+ pedwarn ("overflow in constant expression");
}
-/* Return a 1 if ATTR_NAME and ATTR_ARGS denote a valid language-specific
- attribute for either declaration DECL or type TYPE and 0 otherwise. */
-
-int (*valid_lang_attribute) PARAMS ((tree, tree, tree, tree))
- = default_valid_lang_attribute;
-
-/* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES
- and install them in NODE, which is either a DECL (including a TYPE_DECL)
- or a TYPE. PREFIX_ATTRIBUTES can appear after the declaration specifiers
- and declaration modifiers but before the declaration proper. */
+/* Print a warning if an expression had overflow in folding.
+ Invoke this function on every expression that
+ (1) appears in the source code, and
+ (2) might be a constant expression that overflowed, and
+ (3) is not already checked by convert_and_check;
+ however, do not invoke this function on operands of explicit casts. */
void
-decl_attributes (node, attributes, prefix_attributes)
- tree node, attributes, prefix_attributes;
+overflow_warning (value)
+ tree value;
{
- tree decl = 0, type = 0;
- int is_type = 0;
- tree a;
-
- if (attrtab_idx == 0)
- init_attributes ();
-
- if (DECL_P (node))
+ if ((TREE_CODE (value) == INTEGER_CST
+ || (TREE_CODE (value) == COMPLEX_CST
+ && TREE_CODE (TREE_REALPART (value)) == INTEGER_CST))
+ && TREE_OVERFLOW (value))
{
- decl = node;
- type = TREE_TYPE (decl);
- is_type = TREE_CODE (node) == TYPE_DECL;
+ TREE_OVERFLOW (value) = 0;
+ if (skip_evaluation == 0)
+ warning ("integer overflow in expression");
}
- else if (TYPE_P (node))
- type = node, is_type = 1;
-
-#ifdef PRAGMA_INSERT_ATTRIBUTES
- /* If the code in c-pragma.c wants to insert some attributes then
- allow it to do so. Do this before allowing machine back ends to
- insert attributes, so that they have the opportunity to override
- anything done here. */
- PRAGMA_INSERT_ATTRIBUTES (node, & attributes, & prefix_attributes);
-#endif
-
-#ifdef INSERT_ATTRIBUTES
- INSERT_ATTRIBUTES (node, & attributes, & prefix_attributes);
-#endif
-
- attributes = chainon (prefix_attributes, attributes);
-
- for (a = attributes; a; a = TREE_CHAIN (a))
+ else if ((TREE_CODE (value) == REAL_CST
+ || (TREE_CODE (value) == COMPLEX_CST
+ && TREE_CODE (TREE_REALPART (value)) == REAL_CST))
+ && TREE_OVERFLOW (value))
{
- tree name = TREE_PURPOSE (a);
- tree args = TREE_VALUE (a);
- int i;
- enum attrs id;
-
- for (i = 0; i < attrtab_idx; i++)
- if (attrtab[i].name == name)
- break;
-
- if (i == attrtab_idx)
- {
- if (! valid_machine_attribute (name, args, decl, type)
- && ! (* valid_lang_attribute) (name, args, decl, type))
- warning ("`%s' attribute directive ignored",
- IDENTIFIER_POINTER (name));
- else if (decl != 0)
- type = TREE_TYPE (decl);
- continue;
- }
- else if (attrtab[i].decl_req && decl == 0)
- {
- warning ("`%s' attribute does not apply to types",
- IDENTIFIER_POINTER (name));
- continue;
- }
- else if (list_length (args) < attrtab[i].min
- || list_length (args) > attrtab[i].max)
- {
- error ("wrong number of arguments specified for `%s' attribute",
- IDENTIFIER_POINTER (name));
- continue;
- }
-
- id = attrtab[i].id;
- switch (id)
- {
- case A_PACKED:
- if (is_type)
- TYPE_PACKED (type) = 1;
- else if (TREE_CODE (decl) == FIELD_DECL)
- DECL_PACKED (decl) = 1;
- /* We can't set DECL_PACKED for a VAR_DECL, because the bit is
- used for DECL_REGISTER. It wouldn't mean anything anyway. */
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_NOCOMMON:
- if (TREE_CODE (decl) == VAR_DECL)
- DECL_COMMON (decl) = 0;
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_COMMON:
- if (TREE_CODE (decl) == VAR_DECL)
- DECL_COMMON (decl) = 1;
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_NORETURN:
- if (TREE_CODE (decl) == FUNCTION_DECL)
- TREE_THIS_VOLATILE (decl) = 1;
- else if (TREE_CODE (type) == POINTER_TYPE
- && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
- TREE_TYPE (decl) = type
- = build_pointer_type
- (build_type_variant (TREE_TYPE (type),
- TREE_READONLY (TREE_TYPE (type)), 1));
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_MALLOC:
- if (TREE_CODE (decl) == FUNCTION_DECL)
- DECL_IS_MALLOC (decl) = 1;
- /* ??? TODO: Support types. */
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_UNUSED:
- if (is_type)
- if (decl)
- TREE_USED (decl) = 1;
- else
- TREE_USED (type) = 1;
- else if (TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == VAR_DECL
- || TREE_CODE (decl) == FUNCTION_DECL
- || TREE_CODE (decl) == LABEL_DECL)
- TREE_USED (decl) = 1;
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_CONST:
- if (TREE_CODE (decl) == FUNCTION_DECL)
- TREE_READONLY (decl) = 1;
- else if (TREE_CODE (type) == POINTER_TYPE
- && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
- TREE_TYPE (decl) = type
- = build_pointer_type
- (build_type_variant (TREE_TYPE (type), 1,
- TREE_THIS_VOLATILE (TREE_TYPE (type))));
- else
- warning ( "`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_PURE:
- if (TREE_CODE (decl) == FUNCTION_DECL)
- DECL_IS_PURE (decl) = 1;
- /* ??? TODO: Support types. */
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
-
- case A_T_UNION:
- if (is_type
- && TREE_CODE (type) == UNION_TYPE
- && (decl == 0
- || (TYPE_FIELDS (type) != 0
- && TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type)))))
- TYPE_TRANSPARENT_UNION (type) = 1;
- else if (decl != 0 && TREE_CODE (decl) == PARM_DECL
- && TREE_CODE (type) == UNION_TYPE
- && TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type)))
- DECL_TRANSPARENT_UNION (decl) = 1;
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_CONSTRUCTOR:
- if (TREE_CODE (decl) == FUNCTION_DECL
- && TREE_CODE (type) == FUNCTION_TYPE
- && decl_function_context (decl) == 0)
- {
- DECL_STATIC_CONSTRUCTOR (decl) = 1;
- TREE_USED (decl) = 1;
- }
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_DESTRUCTOR:
- if (TREE_CODE (decl) == FUNCTION_DECL
- && TREE_CODE (type) == FUNCTION_TYPE
- && decl_function_context (decl) == 0)
- {
- DECL_STATIC_DESTRUCTOR (decl) = 1;
- TREE_USED (decl) = 1;
- }
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_MODE:
- if (TREE_CODE (TREE_VALUE (args)) != IDENTIFIER_NODE)
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- else
- {
- int j;
- const char *p = IDENTIFIER_POINTER (TREE_VALUE (args));
- int len = strlen (p);
- enum machine_mode mode = VOIDmode;
- tree typefm;
-
- if (len > 4 && p[0] == '_' && p[1] == '_'
- && p[len - 1] == '_' && p[len - 2] == '_')
- {
- char *newp = (char *) alloca (len - 1);
-
- strcpy (newp, &p[2]);
- newp[len - 4] = '\0';
- p = newp;
- }
-
- /* Give this decl a type with the specified mode.
- First check for the special modes. */
- if (! strcmp (p, "byte"))
- mode = byte_mode;
- else if (!strcmp (p, "word"))
- mode = word_mode;
- else if (! strcmp (p, "pointer"))
- mode = ptr_mode;
- else
- for (j = 0; j < NUM_MACHINE_MODES; j++)
- if (!strcmp (p, GET_MODE_NAME (j)))
- mode = (enum machine_mode) j;
-
- if (mode == VOIDmode)
- error ("unknown machine mode `%s'", p);
- else if (0 == (typefm = type_for_mode (mode,
- TREE_UNSIGNED (type))))
- 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);
- }
- }
- break;
-
- case A_SECTION:
-#ifdef ASM_OUTPUT_SECTION_NAME
- if ((TREE_CODE (decl) == FUNCTION_DECL
- || TREE_CODE (decl) == VAR_DECL)
- && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
- {
- if (TREE_CODE (decl) == VAR_DECL
- && current_function_decl != NULL_TREE
- && ! TREE_STATIC (decl))
- error_with_decl (decl,
- "section attribute cannot be specified for local variables");
- /* The decl may have already been given a section attribute from
- a previous declaration. Ensure they match. */
- else if (DECL_SECTION_NAME (decl) != NULL_TREE
- && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
- TREE_STRING_POINTER (TREE_VALUE (args))) != 0)
- error_with_decl (node,
- "section of `%s' conflicts with previous declaration");
- else
- DECL_SECTION_NAME (decl) = TREE_VALUE (args);
- }
- else
- error_with_decl (node,
- "section attribute not allowed for `%s'");
-#else
- error_with_decl (node,
- "section attributes are not supported for this target");
-#endif
- break;
-
- case A_ALIGNED:
- {
- tree align_expr
- = (args ? TREE_VALUE (args)
- : size_int (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
- int i;
-
- /* Strip any NOPs of any kind. */
- while (TREE_CODE (align_expr) == NOP_EXPR
- || TREE_CODE (align_expr) == CONVERT_EXPR
- || TREE_CODE (align_expr) == NON_LVALUE_EXPR)
- align_expr = TREE_OPERAND (align_expr, 0);
-
- if (TREE_CODE (align_expr) != INTEGER_CST)
- {
- error ("requested alignment is not a constant");
- continue;
- }
-
- if ((i = tree_log2 (align_expr)) == -1)
- error ("requested alignment is not a power of 2");
- else if (i > HOST_BITS_PER_INT - 2)
- error ("requested alignment is too large");
- else if (is_type)
- {
- /* If we have a TYPE_DECL, then copy the type, so that we
- don't accidentally modify a builtin type. See pushdecl. */
- if (decl && TREE_TYPE (decl) != error_mark_node
- && DECL_ORIGINAL_TYPE (decl) == NULL_TREE)
- {
- tree tt = TREE_TYPE (decl);
- DECL_ORIGINAL_TYPE (decl) = tt;
- tt = build_type_copy (tt);
- TYPE_NAME (tt) = decl;
- TREE_USED (tt) = TREE_USED (decl);
- TREE_TYPE (decl) = tt;
- type = tt;
- }
-
- TYPE_ALIGN (type) = (1 << i) * BITS_PER_UNIT;
- TYPE_USER_ALIGN (type) = 1;
- }
- else if (TREE_CODE (decl) != VAR_DECL
- && TREE_CODE (decl) != FIELD_DECL)
- error_with_decl (decl,
- "alignment may not be specified for `%s'");
- else
- {
- DECL_ALIGN (decl) = (1 << i) * BITS_PER_UNIT;
- DECL_USER_ALIGN (decl) = 1;
- }
- }
- break;
+ TREE_OVERFLOW (value) = 0;
+ if (skip_evaluation == 0)
+ warning ("floating point overflow in expression");
+ }
+}
- case A_FORMAT:
- {
- tree format_type_id = TREE_VALUE (args);
- tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
- tree first_arg_num_expr
- = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
- unsigned HOST_WIDE_INT format_num, first_arg_num;
- enum format_type format_type;
- tree argument;
- unsigned int arg_num;
-
- if (TREE_CODE (decl) != FUNCTION_DECL)
- {
- error_with_decl (decl,
- "argument format specified for non-function `%s'");
- continue;
- }
+/* Print a warning if a large constant is truncated to unsigned,
+ or if -Wconversion is used and a constant < 0 is converted to unsigned.
+ Invoke this function on every expression that might be implicitly
+ converted to an unsigned type. */
- if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
- {
- error ("unrecognized format specifier");
- continue;
- }
- else
- {
- const char *p = IDENTIFIER_POINTER (format_type_id);
-
- if (!strcmp (p, "printf") || !strcmp (p, "__printf__"))
- format_type = printf_format_type;
- else if (!strcmp (p, "scanf") || !strcmp (p, "__scanf__"))
- format_type = scanf_format_type;
- else if (!strcmp (p, "strftime")
- || !strcmp (p, "__strftime__"))
- format_type = strftime_format_type;
- else
- {
- warning ("`%s' is an unrecognized format function type", p);
- continue;
- }
- }
+void
+unsigned_conversion_warning (result, operand)
+ tree result, operand;
+{
+ if (TREE_CODE (operand) == INTEGER_CST
+ && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
+ && TREE_UNSIGNED (TREE_TYPE (result))
+ && skip_evaluation == 0
+ && !int_fits_type_p (operand, TREE_TYPE (result)))
+ {
+ if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
+ /* This detects cases like converting -129 or 256 to unsigned char. */
+ warning ("large integer implicitly truncated to unsigned type");
+ else if (warn_conversion)
+ warning ("negative integer implicitly converted to unsigned type");
+ }
+}
- /* Strip any conversions from the string index and first arg number
- and verify they are constants. */
- while (TREE_CODE (format_num_expr) == NOP_EXPR
- || TREE_CODE (format_num_expr) == CONVERT_EXPR
- || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
- format_num_expr = TREE_OPERAND (format_num_expr, 0);
-
- while (TREE_CODE (first_arg_num_expr) == NOP_EXPR
- || TREE_CODE (first_arg_num_expr) == CONVERT_EXPR
- || TREE_CODE (first_arg_num_expr) == NON_LVALUE_EXPR)
- first_arg_num_expr = TREE_OPERAND (first_arg_num_expr, 0);
-
- if (TREE_CODE (format_num_expr) != INTEGER_CST
- || TREE_INT_CST_HIGH (format_num_expr) != 0
- || TREE_CODE (first_arg_num_expr) != INTEGER_CST
- || TREE_INT_CST_HIGH (first_arg_num_expr) != 0)
- {
- error ("format string has invalid operand number");
- continue;
- }
+/* Nonzero if constant C has a value that is permissible
+ for type TYPE (an INTEGER_TYPE). */
- format_num = TREE_INT_CST_LOW (format_num_expr);
- first_arg_num = TREE_INT_CST_LOW (first_arg_num_expr);
- if (first_arg_num != 0 && first_arg_num <= format_num)
- {
- error ("format string arg follows the args to be formatted");
- continue;
- }
-
- /* If a parameter list is specified, verify that the format_num
- argument is actually a string, in case the format attribute
- is in error. */
- argument = TYPE_ARG_TYPES (type);
- if (argument)
- {
- for (arg_num = 1; argument != 0 && arg_num != format_num;
- ++arg_num, argument = TREE_CHAIN (argument))
- ;
-
- if (! argument
- || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
- || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
- != char_type_node))
- {
- error ("format string arg not a string type");
- continue;
- }
-
- else if (first_arg_num != 0)
- {
- /* Verify that first_arg_num points to the last arg,
- the ... */
- while (argument)
- arg_num++, argument = TREE_CHAIN (argument);
-
- if (arg_num != first_arg_num)
- {
- error ("args to be formatted is not '...'");
- continue;
- }
- }
- }
-
- 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);
- break;
- }
-
- case A_FORMAT_ARG:
- {
- tree format_num_expr = TREE_VALUE (args);
- unsigned HOST_WIDE_INT format_num;
- unsigned int arg_num;
- tree argument;
-
- if (TREE_CODE (decl) != FUNCTION_DECL)
- {
- error_with_decl (decl,
- "argument format specified for non-function `%s'");
- continue;
- }
-
- /* Strip any conversions from the first arg number and verify it
- is a constant. */
- while (TREE_CODE (format_num_expr) == NOP_EXPR
- || TREE_CODE (format_num_expr) == CONVERT_EXPR
- || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
- format_num_expr = TREE_OPERAND (format_num_expr, 0);
-
- if (TREE_CODE (format_num_expr) != INTEGER_CST
- || TREE_INT_CST_HIGH (format_num_expr) != 0)
- {
- error ("format string has invalid operand number");
- continue;
- }
-
- format_num = TREE_INT_CST_LOW (format_num_expr);
-
- /* If a parameter list is specified, verify that the format_num
- argument is actually a string, in case the format attribute
- is in error. */
- argument = TYPE_ARG_TYPES (type);
- if (argument)
- {
- for (arg_num = 1; argument != 0 && arg_num != format_num;
- ++arg_num, argument = TREE_CHAIN (argument))
- ;
-
- if (! argument
- || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
- || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
- != char_type_node))
- {
- error ("format string arg not a string type");
- continue;
- }
- }
-
- if (TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) != POINTER_TYPE
- || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl))))
- != char_type_node))
- {
- error ("function does not return string type");
- continue;
- }
-
- record_international_format (DECL_NAME (decl),
- DECL_ASSEMBLER_NAME (decl),
- format_num);
- break;
- }
-
- case A_WEAK:
- declare_weak (decl);
- break;
-
- case A_ALIAS:
- if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
- || (TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl)))
- error_with_decl (decl,
- "`%s' defined both normally and as an alias");
- else if (decl_function_context (decl) == 0)
- {
- tree id;
-
- id = TREE_VALUE (args);
- if (TREE_CODE (id) != STRING_CST)
- {
- error ("alias arg not a string");
- break;
- }
- id = get_identifier (TREE_STRING_POINTER (id));
- /* This counts as a use of the object pointed to. */
- TREE_USED (id) = 1;
-
- if (TREE_CODE (decl) == FUNCTION_DECL)
- DECL_INITIAL (decl) = error_mark_node;
- else
- DECL_EXTERNAL (decl) = 0;
- assemble_alias (decl, id);
- }
- else
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- break;
-
- case A_NO_CHECK_MEMORY_USAGE:
- if (TREE_CODE (decl) != FUNCTION_DECL)
- {
- error_with_decl (decl,
- "`%s' attribute applies only to functions",
- IDENTIFIER_POINTER (name));
- }
- else if (DECL_INITIAL (decl))
- {
- error_with_decl (decl,
- "can't set `%s' attribute after definition",
- IDENTIFIER_POINTER (name));
- }
- else
- DECL_NO_CHECK_MEMORY_USAGE (decl) = 1;
- break;
-
- case A_NO_INSTRUMENT_FUNCTION:
- if (TREE_CODE (decl) != FUNCTION_DECL)
- {
- error_with_decl (decl,
- "`%s' attribute applies only to functions",
- IDENTIFIER_POINTER (name));
- }
- else if (DECL_INITIAL (decl))
- {
- error_with_decl (decl,
- "can't set `%s' attribute after definition",
- IDENTIFIER_POINTER (name));
- }
- else
- DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
- break;
-
- case A_NO_LIMIT_STACK:
- if (TREE_CODE (decl) != FUNCTION_DECL)
- {
- error_with_decl (decl,
- "`%s' attribute applies only to functions",
- IDENTIFIER_POINTER (name));
- }
- else if (DECL_INITIAL (decl))
- {
- error_with_decl (decl,
- "can't set `%s' attribute after definition",
- IDENTIFIER_POINTER (name));
- }
- else
- DECL_NO_LIMIT_STACK (decl) = 1;
- break;
- }
- }
-}
-
-/* Split SPECS_ATTRS, a list of declspecs and prefix attributes, into two
- lists. SPECS_ATTRS may also be just a typespec (eg: RECORD_TYPE).
-
- The head of the declspec list is stored in DECLSPECS.
- The head of the attribute list is stored in PREFIX_ATTRIBUTES.
-
- Note that attributes in SPECS_ATTRS are stored in the TREE_PURPOSE of
- the list elements. We drop the containing TREE_LIST nodes and link the
- resulting attributes together the way decl_attributes expects them. */
-
-void
-split_specs_attrs (specs_attrs, declspecs, prefix_attributes)
- tree specs_attrs;
- tree *declspecs, *prefix_attributes;
-{
- tree t, s, a, next, specs, attrs;
-
- /* This can happen after an __extension__ in pedantic mode. */
- if (specs_attrs != NULL_TREE
- && TREE_CODE (specs_attrs) == INTEGER_CST)
- {
- *declspecs = NULL_TREE;
- *prefix_attributes = NULL_TREE;
- return;
- }
-
- /* This can happen in c++ (eg: decl: typespec initdecls ';'). */
- if (specs_attrs != NULL_TREE
- && TREE_CODE (specs_attrs) != TREE_LIST)
- {
- *declspecs = specs_attrs;
- *prefix_attributes = NULL_TREE;
- return;
- }
-
- /* Remember to keep the lists in the same order, element-wise. */
-
- specs = s = NULL_TREE;
- attrs = a = NULL_TREE;
- for (t = specs_attrs; t; t = next)
- {
- next = TREE_CHAIN (t);
- /* Declspecs have a non-NULL TREE_VALUE. */
- if (TREE_VALUE (t) != NULL_TREE)
- {
- if (specs == NULL_TREE)
- specs = s = t;
- else
- {
- TREE_CHAIN (s) = t;
- s = t;
- }
- }
- else
- {
- if (attrs == NULL_TREE)
- attrs = a = TREE_PURPOSE (t);
- else
- {
- TREE_CHAIN (a) = TREE_PURPOSE (t);
- a = TREE_PURPOSE (t);
- }
- /* More attrs can be linked here, move A to the end. */
- while (TREE_CHAIN (a) != NULL_TREE)
- a = TREE_CHAIN (a);
- }
- }
-
- /* Terminate the lists. */
- if (s != NULL_TREE)
- TREE_CHAIN (s) = NULL_TREE;
- if (a != NULL_TREE)
- TREE_CHAIN (a) = NULL_TREE;
-
- /* All done. */
- *declspecs = specs;
- *prefix_attributes = attrs;
-}
-
-/* Strip attributes from SPECS_ATTRS, a list of declspecs and attributes.
- This function is used by the parser when a rule will accept attributes
- in a particular position, but we don't want to support that just yet.
-
- A warning is issued for every ignored attribute. */
-
-tree
-strip_attrs (specs_attrs)
- tree specs_attrs;
-{
- tree specs, attrs;
-
- split_specs_attrs (specs_attrs, &specs, &attrs);
-
- while (attrs)
- {
- warning ("`%s' attribute ignored",
- IDENTIFIER_POINTER (TREE_PURPOSE (attrs)));
- attrs = TREE_CHAIN (attrs);
- }
-
- return specs;
-}
-\f
-/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
- a parameter list. */
-
-/* The meaningfully distinct length modifiers for format checking recognised
- by GCC. */
-enum format_lengths
-{
- FMT_LEN_none,
- FMT_LEN_hh,
- FMT_LEN_h,
- FMT_LEN_l,
- FMT_LEN_ll,
- FMT_LEN_L,
- FMT_LEN_z,
- FMT_LEN_t,
- FMT_LEN_j,
- FMT_LEN_MAX
-};
-
-
-/* The standard versions in which various format features appeared. */
-enum format_std_version
-{
- STD_C89,
- STD_C94,
- STD_C99,
- STD_EXT
-};
-
-/* The C standard version C++ is treated as equivalent to
- or inheriting from, for the purpose of format features supported. */
-#define CPLUSPLUS_STD_VER STD_C89
-/* The C standard version we are checking formats against when pedantic. */
-#define C_STD_VER (c_language == clk_cplusplus \
- ? CPLUSPLUS_STD_VER \
- : (flag_isoc99 \
- ? STD_C99 \
- : (flag_isoc94 ? STD_C94 : STD_C89)))
-/* The name to give to the standard version we are warning about when
- pedantic. FEATURE_VER is the version in which the feature warned out
- appeared, which is higher than C_STD_VER. */
-#define C_STD_NAME(FEATURE_VER) (c_language == clk_cplusplus \
- ? "ISO C++" \
- : ((FEATURE_VER) == STD_EXT \
- ? "ISO C" \
- : "ISO C89"))
-
-/* Flags that may apply to a particular kind of format checked by GCC. */
-enum
-{
- /* This format converts arguments of types determined by the
- format string. */
- FMT_FLAG_ARG_CONVERT = 1,
- /* The scanf allocation 'a' kludge applies to this format kind. */
- FMT_FLAG_SCANF_A_KLUDGE = 2,
- /* A % during parsing a specifier is allowed to be a modified % rather
- that indicating the format is broken and we are out-of-sync. */
- FMT_FLAG_FANCY_PERCENT_OK = 4,
- /* With $ operand numbers, it is OK to reference the same argument more
- than once. */
- FMT_FLAG_DOLLAR_MULTIPLE = 8
- /* Not included here: details of whether width or precision may occur
- (controlled by width_char and precision_char); details of whether
- '*' can be used for these (width_type and precision_type); details
- of whether length modifiers can occur (length_char_specs); details
- of when $ operand numbers are allowed (always, for the formats
- supported, if arguments are converted). */
-};
-
-
-/* Structure describing a length modifier supported in format checking, and
- possibly a doubled version such as "hh". */
-typedef struct
-{
- /* Name of the single-character length modifier. */
- const char *name;
- /* Index into a format_char_info.types array. */
- enum format_lengths index;
- /* Standard version this length appears in. */
- enum format_std_version std;
- /* Same, if the modifier can be repeated, or NULL if it can't. */
- const char *double_name;
- enum format_lengths double_index;
- enum format_std_version double_std;
-} format_length_info;
-
-
-/* Structure desribing the combination of a conversion specifier
- (or a set of specifiers which act identically) and a length modifier. */
-typedef struct
-{
- /* The standard version this combination of length and type appeared in.
- This is only relevant if greater than those for length and type
- individually; otherwise it is ignored. */
- enum format_std_version std;
- /* The name to use for the type, if different from that generated internally
- (e.g., "signed size_t"). */
- const char *name;
- /* The type itself. */
- tree *type;
-} format_type_detail;
-
-
-/* Macros to fill out tables of these. */
-#define BADLEN { 0, NULL, NULL }
-#define NOLENGTHS { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
-
-
-/* Structure desribing a format conversion specifier (or a set of specifiers
- which act identically), and the length modifiers used with it. */
-typedef struct
-{
- const char *format_chars;
- int pointer_count;
- enum format_std_version std;
- /* Types accepted for each length modifier. */
- format_type_detail types[FMT_LEN_MAX];
- /* List of other modifier characters allowed with these specifiers.
- This lists flags, and additionally "w" for width, "p" for precision,
- "a" for scanf "a" allocation extension (not applicable in C99 mode),
- "*" for scanf suppression, and "E" and "O" for those strftime
- modifiers. */
- const char *flag_chars;
- /* List of additional flags describing these conversion specifiers.
- "c" for generic character pointers being allowed, "2" for strftime
- two digit year formats, "3" for strftime formats giving two digit
- years in some locales, "4" for "2" which becomes "3" with an "E" modifier,
- "o" if use of strftime "O" is a GNU extension beyond C99,
- "W" if the argument is a pointer which is dereferenced and written into,
- "i" for printf integer formats where the '0' flag is ignored with
- precision, and "[" for the starting character of a scanf scanset. */
- const char *flags2;
-} format_char_info;
-
-
-/* Structure describing a flag accepted by some kind of format. */
-typedef struct
-{
- /* The flag character in question (0 for end of array). */
- int flag_char;
- /* Zero if this entry describes the flag character in general, or a
- non-zero character that may be found in flags2 if it describes the
- flag when used with certain formats only. If the latter, only
- the first such entry found that applies to the current conversion
- specifier is used; the values of `name' and `long_name' it supplies
- will be used, if non-NULL and the standard version is higher than
- the unpredicated one, for any pedantic warning. For example, 'o'
- for strftime formats (meaning 'O' is an extension over C99). */
- int predicate;
- /* The name to use for this flag in diagnostic messages. For example,
- N_("`0' flag"), N_("field width"). */
- const char *name;
- /* Long name for this flag in diagnostic messages; currently only used for
- "ISO C does not support ...". For example, N_("the `I' printf flag"). */
- const char *long_name;
- /* The standard version in which it appeared. */
- enum format_std_version std;
-} format_flag_spec;
-
-
-/* Structure describing a combination of flags that is bad for some kind
- of format. */
-typedef struct
-{
- /* The first flag character in question (0 for end of array). */
- int flag_char1;
- /* The second flag character. */
- int flag_char2;
- /* Non-zero if the message should say that the first flag is ignored with
- the second, zero if the combination should simply be objected to. */
- int ignored;
- /* Zero if this entry applies whenever this flag combination occurs,
- a non-zero character from flags2 if it only applies in some
- circumstances (e.g. 'i' for printf formats ignoring 0 with precision). */
- int predicate;
-} format_flag_pair;
-
-
-/* Structure describing a particular kind of format processed by GCC. */
-typedef struct
-{
- /* The name of this kind of format, for use in diagnostics. */
- const char *name;
- /* Specifications of the length modifiers accepted; possibly NULL. */
- const format_length_info *length_char_specs;
- /* Details of the conversion specification characters accepted. */
- const format_char_info *conversion_specs;
- /* String listing the flag characters that are accepted. */
- const char *flag_chars;
- /* String listing modifier characters (strftime) accepted. May be NULL. */
- const char *modifier_chars;
- /* Details of the flag characters, including pseudo-flags. */
- const format_flag_spec *flag_specs;
- /* Details of bad combinations of flags. */
- const format_flag_pair *bad_flag_pairs;
- /* Flags applicable to this kind of format. */
- int flags;
- /* Flag character to treat a width as, or 0 if width not used. */
- int width_char;
- /* Flag character to treat a precision as, or 0 if precision not used. */
- int precision_char;
- /* If a flag character has the effect of suppressing the conversion of
- an argument ('*' in scanf), that flag character, otherwise 0. */
- int suppression_char;
- /* Flag character to treat a length modifier as (ignored if length
- modifiers not used). Need not be placed in flag_chars for conversion
- specifiers, but is used to check for bad combinations such as length
- modifier with assignment suppression in scanf. */
- int length_code_char;
- /* Pointer to type of argument expected if '*' is used for a width,
- or NULL if '*' not used for widths. */
- tree *width_type;
- /* Pointer to type of argument expected if '*' is used for a precision,
- or NULL if '*' not used for precisions. */
- tree *precision_type;
-} format_kind_info;
-
-
-/* Structure describing details of a type expected in format checking,
- and the type to check against it. */
-typedef struct format_wanted_type
-{
- /* The type wanted. */
- tree wanted_type;
- /* The name of this type to use in diagnostics. */
- const char *wanted_type_name;
- /* The level of indirection through pointers at which this type occurs. */
- int pointer_count;
- /* Whether, when pointer_count is 1, to allow any character type when
- pedantic, rather than just the character or void type specified. */
- int char_lenient_flag;
- /* Whether the argument, dereferenced once, is written into and so the
- argument must not be a pointer to a const-qualified type. */
- int writing_in_flag;
- /* If warnings should be of the form "field precision is not type int",
- the name to use (in this case "field precision"), otherwise NULL,
- for "%s format, %s arg" type messages. If (in an extension), this
- is a pointer type, wanted_type_name should be set to include the
- terminating '*' characters of the type name to give a correct
- message. */
- const char *name;
- /* The actual parameter to check against the wanted type. */
- tree param;
- /* The argument number of that parameter. */
- int arg_num;
- /* The next type to check for this format conversion, or NULL if none. */
- struct format_wanted_type *next;
-} format_wanted_type;
-
-
-static const format_length_info printf_length_specs[] =
-{
- { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
- { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C99 },
- { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
- { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
- { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
- { "Z", FMT_LEN_z, STD_EXT, NULL, 0, 0 },
- { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
- { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
- { NULL, 0, 0, NULL, 0, 0 }
-};
-
-
-/* This differs from printf_length_specs only in that "Z" is not accepted. */
-static const format_length_info scanf_length_specs[] =
-{
- { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
- { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C99 },
- { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
- { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
- { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
- { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
- { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
- { NULL, 0, 0, NULL, 0, 0 }
-};
-
-
-static const format_flag_spec printf_flag_specs[] =
-{
- { ' ', 0, N_("` ' flag"), N_("the ` ' printf flag"), STD_C89 },
- { '+', 0, N_("`+' flag"), N_("the `+' printf flag"), STD_C89 },
- { '#', 0, N_("`#' flag"), N_("the `#' printf flag"), STD_C89 },
- { '0', 0, N_("`0' flag"), N_("the `0' printf flag"), STD_C89 },
- { '-', 0, N_("`-' flag"), N_("the `-' printf flag"), STD_C89 },
- { '\'', 0, N_("`'' flag"), N_("the `'' printf flag"), STD_EXT },
- { 'I', 0, N_("`I' flag"), N_("the `I' printf flag"), STD_EXT },
- { 'w', 0, N_("field width"), N_("field width in printf format"), STD_C89 },
- { 'p', 0, N_("precision"), N_("precision in printf format"), STD_C89 },
- { 'L', 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
- { 0, 0, NULL, NULL, 0 }
-};
-
-
-static const format_flag_pair printf_flag_pairs[] =
-{
- { ' ', '+', 1, 0 },
- { '0', '-', 1, 0 },
- { '0', 'p', 1, 'i' },
- { 0, 0, 0, 0 }
-};
-
-
-static const format_flag_spec scanf_flag_specs[] =
-{
- { '*', 0, N_("assignment suppression"), N_("assignment suppression"), STD_C89 },
- { 'a', 0, N_("`a' flag"), N_("the `a' scanf flag"), STD_EXT },
- { 'w', 0, N_("field width"), N_("field width in scanf format"), STD_C89 },
- { 'L', 0, N_("length modifier"), N_("length modifier in scanf format"), STD_C89 },
- { '\'', 0, N_("`'' flag"), N_("the `'' scanf flag"), STD_EXT },
- { 'I', 0, N_("`I' flag"), N_("the `I' scanf flag"), STD_EXT },
- { 0, 0, NULL, NULL, 0 }
-};
-
-
-static const format_flag_pair scanf_flag_pairs[] =
-{
- { '*', 'L', 0, 0 },
- { 0, 0, 0, 0 }
-};
-
-
-static const format_flag_spec strftime_flag_specs[] =
-{
- { '_', 0, N_("`_' flag"), N_("the `_' strftime flag"), STD_EXT },
- { '-', 0, N_("`-' flag"), N_("the `-' strftime flag"), STD_EXT },
- { '0', 0, N_("`0' flag"), N_("the `0' strftime flag"), STD_EXT },
- { '^', 0, N_("`^' flag"), N_("the `^' strftime flag"), STD_EXT },
- { '#', 0, N_("`#' flag"), N_("the `#' strftime flag"), STD_EXT },
- { 'w', 0, N_("field width"), N_("field width in strftime format"), STD_EXT },
- { 'E', 0, N_("`E' modifier"), N_("the `E' strftime modifier"), STD_C99 },
- { 'O', 0, N_("`O' modifier"), N_("the `O' strftime modifier"), STD_C99 },
- { 'O', 'o', NULL, N_("the `O' modifier"), STD_EXT },
- { 0, 0, NULL, NULL, 0 }
-};
-
-
-static const format_flag_pair strftime_flag_pairs[] =
-{
- { 'E', 'O', 0, 0 },
- { '_', '-', 0, 0 },
- { '_', '0', 0, 0 },
- { '-', '0', 0, 0 },
- { '^', '#', 0, 0 },
- { 0, 0, 0, 0 }
-};
-
-
-#define T_I &integer_type_node
-#define T89_I { STD_C89, NULL, T_I }
-#define T99_I { STD_C99, NULL, T_I }
-#define T_L &long_integer_type_node
-#define T89_L { STD_C89, NULL, T_L }
-#define T_LL &long_long_integer_type_node
-#define T99_LL { STD_C99, NULL, T_LL }
-#define TEX_LL { STD_EXT, NULL, T_LL }
-#define T_S &short_integer_type_node
-#define T89_S { STD_C89, NULL, T_S }
-#define T_UI &unsigned_type_node
-#define T89_UI { STD_C89, NULL, T_UI }
-#define T99_UI { STD_C99, NULL, T_UI }
-#define T_UL &long_unsigned_type_node
-#define T89_UL { STD_C89, NULL, T_UL }
-#define T_ULL &long_long_unsigned_type_node
-#define T99_ULL { STD_C99, NULL, T_ULL }
-#define TEX_ULL { STD_EXT, NULL, T_ULL }
-#define T_US &short_unsigned_type_node
-#define T89_US { STD_C89, NULL, T_US }
-#define T_F &float_type_node
-#define T89_F { STD_C89, NULL, T_F }
-#define T99_F { STD_C99, NULL, T_F }
-#define T_D &double_type_node
-#define T89_D { STD_C89, NULL, T_D }
-#define T99_D { STD_C99, NULL, T_D }
-#define T_LD &long_double_type_node
-#define T89_LD { STD_C89, NULL, T_LD }
-#define T99_LD { STD_C99, NULL, T_LD }
-#define T_C &char_type_node
-#define T89_C { STD_C89, NULL, T_C }
-#define T_SC &signed_char_type_node
-#define T99_SC { STD_C99, NULL, T_SC }
-#define T_UC &unsigned_char_type_node
-#define T99_UC { STD_C99, NULL, T_UC }
-#define T_V &void_type_node
-#define T89_V { STD_C89, NULL, T_V }
-#define T_W &wchar_type_node
-#define T94_W { STD_C94, "wchar_t", T_W }
-#define TEX_W { STD_EXT, "wchar_t", T_W }
-#define T_WI &wint_type_node
-#define T94_WI { STD_C94, "wint_t", T_WI }
-#define TEX_WI { STD_EXT, "wint_t", T_WI }
-#define T_ST &c_size_type_node
-#define T99_ST { STD_C99, "size_t", T_ST }
-#define T_SST &signed_size_type_node
-#define T99_SST { STD_C99, "signed size_t", T_SST }
-#define T_PD &ptrdiff_type_node
-#define T99_PD { STD_C99, "ptrdiff_t", T_PD }
-#define T_UPD &unsigned_ptrdiff_type_node
-#define T99_UPD { STD_C99, "unsigned ptrdiff_t", T_UPD }
-#define T_IM &intmax_type_node
-#define T99_IM { STD_C99, "intmax_t", T_IM }
-#define T_UIM &uintmax_type_node
-#define T99_UIM { STD_C99, "uintmax_t", T_UIM }
-
-static const format_char_info print_char_table[] =
-{
- /* C89 conversion specifiers. */
- { "di", 0, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "-wp0 +'I", "i" },
- { "oxX", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0#", "i" },
- { "u", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0'I", "i" },
- { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#'", "" },
- { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "" },
- { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "" },
- { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "c" },
- { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c" },
- { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "", "W" },
- /* C99 conversion specifiers. */
- { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#'", "" },
- { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "" },
- /* X/Open conversion specifiers. */
- { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "" },
- { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "" },
- /* GNU conversion specifiers. */
- { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "" },
- { NULL, 0, 0, NOLENGTHS, NULL, NULL }
-};
-
-static const format_char_info scan_char_table[] =
-{
- /* C89 conversion specifiers. */
- { "di", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "*w'I", "W" },
- { "u", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "*w'I", "W" },
- { "oxX", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "*w", "W" },
- { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "*w'", "W" },
- { "c", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "cW" },
- { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW" },
- { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW[" },
- { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W" },
- { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "", "W" },
- /* C99 conversion specifiers. */
- { "FaA", 1, STD_C99, { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "*w'", "W" },
- /* X/Open conversion specifiers. */
- { "C", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W" },
- { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "W" },
- { NULL, 0, 0, NOLENGTHS, NULL, NULL }
-};
-
-static format_char_info time_char_table[] =
-{
- /* C89 conversion specifiers. */
- { "ABZab", 0, STD_C89, NOLENGTHS, "^#", "" },
- { "cx", 0, STD_C89, NOLENGTHS, "E", "3" },
- { "HIMSUWdmw", 0, STD_C89, NOLENGTHS, "-_0Ow", "" },
- { "j", 0, STD_C89, NOLENGTHS, "-_0Ow", "o" },
- { "p", 0, STD_C89, NOLENGTHS, "#", "" },
- { "X", 0, STD_C89, NOLENGTHS, "E", "" },
- { "y", 0, STD_C89, NOLENGTHS, "EO-_0w", "4" },
- { "Y", 0, STD_C89, NOLENGTHS, "-_0EOw", "o" },
- { "%", 0, STD_C89, NOLENGTHS, "", "" },
- /* C99 conversion specifiers. */
- { "C", 0, STD_C99, NOLENGTHS, "-_0EOw", "o" },
- { "D", 0, STD_C99, NOLENGTHS, "", "2" },
- { "eVu", 0, STD_C99, NOLENGTHS, "-_0Ow", "" },
- { "FRTnrt", 0, STD_C99, NOLENGTHS, "", "" },
- { "g", 0, STD_C99, NOLENGTHS, "O-_0w", "2o" },
- { "G", 0, STD_C99, NOLENGTHS, "-_0Ow", "o" },
- { "h", 0, STD_C99, NOLENGTHS, "^#", "" },
- { "z", 0, STD_C99, NOLENGTHS, "O", "o" },
- /* GNU conversion specifiers. */
- { "kls", 0, STD_EXT, NOLENGTHS, "-_0Ow", "" },
- { "P", 0, STD_EXT, NOLENGTHS, "", "" },
- { NULL, 0, 0, NOLENGTHS, NULL, NULL }
-};
-
-
-/* This must be in the same order as enum format_type. */
-static const format_kind_info format_types[] =
-{
- { "printf", printf_length_specs, print_char_table, " +#0-'I", NULL,
- printf_flag_specs, printf_flag_pairs,
- FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE, 'w', 'p', 0, 'L',
- &integer_type_node, &integer_type_node
- },
- { "scanf", scanf_length_specs, scan_char_table, "*'I", NULL,
- scanf_flag_specs, scanf_flag_pairs,
- FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE, 'w', 0, '*', 'L',
- NULL, NULL
- },
- { "strftime", NULL, time_char_table, "_-0^#", "EO",
- strftime_flag_specs, strftime_flag_pairs,
- FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0,
- NULL, NULL
- }
-};
-
-
-typedef struct function_format_info
-{
- struct function_format_info *next; /* next structure on the list */
- tree name; /* identifier such as "printf" */
- tree assembler_name; /* optional mangled identifier (for C++) */
- enum format_type format_type; /* type of format (printf, scanf, etc.) */
- int format_num; /* number of format argument */
- int first_arg_num; /* number of first arg (zero for varargs) */
-} function_format_info;
-
-static function_format_info *function_format_list = NULL;
-
-typedef struct international_format_info
-{
- struct international_format_info *next; /* next structure on the list */
- tree name; /* identifier such as "gettext" */
- tree assembler_name; /* optional mangled identifier (for C++) */
- int format_num; /* number of format argument */
-} international_format_info;
-
-static international_format_info *international_format_list = NULL;
-
-/* Structure detailing the results of checking a format function call
- where the format expression may be a conditional expression with
- many leaves resulting from nested conditional expressions. */
-typedef struct
-{
- /* Number of leaves of the format argument that could not be checked
- as they were not string literals. */
- int number_non_literal;
- /* Number of leaves of the format argument that were null pointers or
- string literals, but had extra format arguments. */
- int number_extra_args;
- /* Number of leaves of the format argument that were null pointers or
- string literals, but had extra format arguments and used $ operand
- numbers. */
- int number_dollar_extra_args;
- /* Number of leaves of the format argument that were wide string
- literals. */
- int number_wide;
- /* Number of leaves of the format argument that were empty strings. */
- int number_empty;
- /* Number of leaves of the format argument that were unterminated
- strings. */
- int number_unterminated;
- /* Number of leaves of the format argument that were not counted above. */
- int number_other;
-} format_check_results;
-
-static void check_format_info PARAMS ((int *, function_format_info *, tree));
-static void check_format_info_recurse PARAMS ((int *, format_check_results *,
- function_format_info *, tree,
- tree, int));
-static void check_format_info_main PARAMS ((int *, format_check_results *,
- function_format_info *,
- const char *, int, tree, int));
-static void status_warning PARAMS ((int *, const char *, ...))
- ATTRIBUTE_PRINTF_2;
-
-static void init_dollar_format_checking PARAMS ((int, tree));
-static int maybe_read_dollar_number PARAMS ((int *, const char **, int,
- tree, tree *,
- const format_kind_info *));
-static void finish_dollar_format_checking PARAMS ((int *, format_check_results *));
-
-static const format_flag_spec *get_flag_spec PARAMS ((const format_flag_spec *,
- int, const char *));
-
-static void check_format_types PARAMS ((int *, format_wanted_type *));
-static int is_valid_printf_arglist PARAMS ((tree));
-static rtx c_expand_builtin PARAMS ((tree, rtx, enum machine_mode, enum expand_modifier));
-static rtx c_expand_builtin_printf PARAMS ((tree, rtx, enum machine_mode,
- enum expand_modifier, int));
-
-/* Initialize the table of functions to perform format checking on.
- The ISO C functions are always checked (whether <stdio.h> is
- included or not), since it is common to call printf without
- including <stdio.h>. There shouldn't be a problem with this,
- since ISO C reserves these function names whether you include the
- header file or not. In any case, the checking is harmless. With
- -ffreestanding, these default attributes are disabled, and must be
- specified manually if desired.
-
- Also initialize the name of function that modify the format string for
- internationalization purposes. */
-
-void
-init_function_format_info ()
-{
- if (flag_hosted)
- {
- /* Functions from ISO/IEC 9899:1990. */
- record_function_format (get_identifier ("printf"), NULL_TREE,
- printf_format_type, 1, 2);
- record_function_format (get_identifier ("__builtin_printf"), NULL_TREE,
- printf_format_type, 1, 2);
- record_function_format (get_identifier ("fprintf"), NULL_TREE,
- printf_format_type, 2, 3);
- record_function_format (get_identifier ("sprintf"), NULL_TREE,
- printf_format_type, 2, 3);
- record_function_format (get_identifier ("scanf"), NULL_TREE,
- scanf_format_type, 1, 2);
- record_function_format (get_identifier ("fscanf"), NULL_TREE,
- scanf_format_type, 2, 3);
- record_function_format (get_identifier ("sscanf"), NULL_TREE,
- scanf_format_type, 2, 3);
- record_function_format (get_identifier ("vprintf"), NULL_TREE,
- printf_format_type, 1, 0);
- record_function_format (get_identifier ("vfprintf"), NULL_TREE,
- printf_format_type, 2, 0);
- record_function_format (get_identifier ("vsprintf"), NULL_TREE,
- printf_format_type, 2, 0);
- record_function_format (get_identifier ("strftime"), NULL_TREE,
- strftime_format_type, 3, 0);
- }
-
- if (flag_hosted && flag_isoc99)
- {
- /* ISO C99 adds the snprintf and vscanf family functions. */
- record_function_format (get_identifier ("snprintf"), NULL_TREE,
- printf_format_type, 3, 4);
- record_function_format (get_identifier ("vsnprintf"), NULL_TREE,
- printf_format_type, 3, 0);
- record_function_format (get_identifier ("vscanf"), NULL_TREE,
- scanf_format_type, 1, 0);
- record_function_format (get_identifier ("vfscanf"), NULL_TREE,
- scanf_format_type, 2, 0);
- record_function_format (get_identifier ("vsscanf"), NULL_TREE,
- scanf_format_type, 2, 0);
- }
-
- if (flag_hosted && flag_noniso_default_format_attributes)
- {
- /* Uniforum/GNU gettext functions, not in ISO C. */
- record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
- record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
- record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
- }
-}
-
-/* Record information for argument format checking. FUNCTION_IDENT is
- the identifier node for the name of the function to check (its decl
- need not exist yet).
- FORMAT_TYPE specifies the type of format checking. FORMAT_NUM is the number
- of the argument which is the format control string (starting from 1).
- FIRST_ARG_NUM is the number of the first actual argument to check
- against the format string, or zero if no checking is not be done
- (e.g. for varargs such as vfprintf). */
-
-static void
-record_function_format (name, assembler_name, format_type,
- format_num, first_arg_num)
- tree name;
- tree assembler_name;
- enum format_type format_type;
- int format_num;
- int first_arg_num;
-{
- function_format_info *info;
-
- /* Re-use existing structure if it's there. */
-
- for (info = function_format_list; info; info = info->next)
- {
- if (info->name == name && info->assembler_name == assembler_name)
- break;
- }
- if (! info)
- {
- info = (function_format_info *) xmalloc (sizeof (function_format_info));
- info->next = function_format_list;
- function_format_list = info;
-
- info->name = name;
- info->assembler_name = assembler_name;
- }
-
- info->format_type = format_type;
- info->format_num = format_num;
- info->first_arg_num = first_arg_num;
-}
-
-/* Record information for the names of function that modify the format
- argument to format functions. FUNCTION_IDENT is the identifier node for
- the name of the function (its decl need not exist yet) and FORMAT_NUM is
- the number of the argument which is the format control string (starting
- from 1). */
-
-static void
-record_international_format (name, assembler_name, format_num)
- tree name;
- tree assembler_name;
- int format_num;
-{
- international_format_info *info;
-
- /* Re-use existing structure if it's there. */
-
- for (info = international_format_list; info; info = info->next)
- {
- if (info->name == name && info->assembler_name == assembler_name)
- break;
- }
-
- if (! info)
- {
- info
- = (international_format_info *)
- xmalloc (sizeof (international_format_info));
- info->next = international_format_list;
- international_format_list = info;
-
- info->name = name;
- info->assembler_name = assembler_name;
- }
-
- info->format_num = format_num;
-}
-\f
-/* Check the argument list of a call to printf, scanf, etc.
- NAME is the function identifier.
- ASSEMBLER_NAME is the function's assembler identifier.
- (Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.)
- PARAMS is the list of argument values. Also, if -Wmissing-format-attribute,
- warn for calls to vprintf or vscanf in functions with no such format
- attribute themselves. */
-
-void
-check_function_format (status, name, assembler_name, params)
- int *status;
- tree name;
- tree assembler_name;
- tree params;
-{
- function_format_info *info;
-
- /* See if this function is a format function. */
- for (info = function_format_list; info; info = info->next)
- {
- if (info->assembler_name
- ? (info->assembler_name == assembler_name)
- : (info->name == name))
- {
- /* Yup; check it. */
- 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;
-
- if (status)
- *status = 1;
- else
- {
- VA_START (ap, msgid);
-
-#ifndef ANSI_PROTOTYPES
- status = va_arg (ap, int *);
- msgid = va_arg (ap, const char *);
-#endif
-
- /* This duplicates the warning function behavior. */
- set_diagnostic_context
- (&dc, msgid, &ap, input_filename, lineno, /* warn = */ 1);
- report_diagnostic (&dc);
-
- va_end (ap);
- }
-}
-
-/* Variables used by the checking of $ operand number formats. */
-static char *dollar_arguments_used = NULL;
-static int dollar_arguments_alloc = 0;
-static int dollar_arguments_count;
-static int dollar_first_arg_num;
-static int dollar_max_arg_used;
-static int dollar_format_warned;
-
-/* Initialize the checking for a format string that may contain $
- parameter number specifications; we will need to keep track of whether
- each parameter has been used. FIRST_ARG_NUM is the number of the first
- argument that is a parameter to the format, or 0 for a vprintf-style
- function; PARAMS is the list of arguments starting at this argument. */
-
-static void
-init_dollar_format_checking (first_arg_num, params)
- int first_arg_num;
- tree params;
-{
- dollar_first_arg_num = first_arg_num;
- dollar_arguments_count = 0;
- dollar_max_arg_used = 0;
- dollar_format_warned = 0;
- if (first_arg_num > 0)
- {
- while (params)
- {
- dollar_arguments_count++;
- params = TREE_CHAIN (params);
- }
- }
- if (dollar_arguments_alloc < dollar_arguments_count)
- {
- if (dollar_arguments_used)
- free (dollar_arguments_used);
- dollar_arguments_alloc = dollar_arguments_count;
- dollar_arguments_used = xmalloc (dollar_arguments_alloc);
- }
- if (dollar_arguments_alloc)
- memset (dollar_arguments_used, 0, dollar_arguments_alloc);
-}
-
-
-/* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED
- is set, it is an error if one is not found; otherwise, it is OK. If
- such a number is found, check whether it is within range and mark that
- numbered operand as being used for later checking. Returns the operand
- number if found and within range, zero if no such number was found and
- this is OK, or -1 on error. PARAMS points to the first operand of the
- format; PARAM_PTR is made to point to the parameter referred to. If
- a $ format is found, *FORMAT is updated to point just after it. */
-
-static int
-maybe_read_dollar_number (status, format, dollar_needed, params, param_ptr,
- fki)
- int *status;
- const char **format;
- int dollar_needed;
- tree params;
- tree *param_ptr;
- const format_kind_info *fki;
-{
- int argnum;
- int overflow_flag;
- const char *fcp = *format;
- if (*fcp < '0' || *fcp > '9')
- {
- if (dollar_needed)
- {
- status_warning (status, "missing $ operand number in format");
- return -1;
- }
- else
- return 0;
- }
- argnum = 0;
- overflow_flag = 0;
- while (*fcp >= '0' && *fcp <= '9')
- {
- int nargnum;
- nargnum = 10 * argnum + (*fcp - '0');
- if (nargnum < 0 || nargnum / 10 != argnum)
- overflow_flag = 1;
- argnum = nargnum;
- fcp++;
- }
- if (*fcp != '$')
- {
- if (dollar_needed)
- {
- status_warning (status, "missing $ operand number in format");
- return -1;
- }
- else
- return 0;
- }
- *format = fcp + 1;
- if (pedantic && !dollar_format_warned)
- {
- status_warning (status,
- "%s does not support %%n$ operand number formats",
- C_STD_NAME (STD_EXT));
- dollar_format_warned = 1;
- }
- if (overflow_flag || argnum == 0
- || (dollar_first_arg_num && argnum > dollar_arguments_count))
- {
- status_warning (status, "operand number out of range in format");
- return -1;
- }
- if (argnum > dollar_max_arg_used)
- dollar_max_arg_used = argnum;
- /* For vprintf-style functions we may need to allocate more memory to
- track which arguments are used. */
- while (dollar_arguments_alloc < dollar_max_arg_used)
- {
- int nalloc;
- nalloc = 2 * dollar_arguments_alloc + 16;
- dollar_arguments_used = xrealloc (dollar_arguments_used, nalloc);
- memset (dollar_arguments_used + dollar_arguments_alloc, 0,
- nalloc - dollar_arguments_alloc);
- dollar_arguments_alloc = nalloc;
- }
- if (!(fki->flags & FMT_FLAG_DOLLAR_MULTIPLE)
- && dollar_arguments_used[argnum - 1] == 1)
- {
- dollar_arguments_used[argnum - 1] = 2;
- status_warning (status,
- "format argument %d used more than once in %s format",
- argnum, fki->name);
- }
- else
- dollar_arguments_used[argnum - 1] = 1;
- if (dollar_first_arg_num)
- {
- int i;
- *param_ptr = params;
- for (i = 1; i < argnum && *param_ptr != 0; i++)
- *param_ptr = TREE_CHAIN (*param_ptr);
-
- if (*param_ptr == 0)
- {
- /* This case shouldn't be caught here. */
- abort ();
- }
- }
- else
- *param_ptr = 0;
- return argnum;
-}
-
-
-/* Finish the checking for a format string that used $ operand number formats
- instead of non-$ formats. We check for unused operands before used ones
- (a serious error, since the implementation of the format function
- can't know what types to pass to va_arg to find the later arguments).
- and for unused operands at the end of the format (if we know how many
- arguments the format had, so not for vprintf). If there were operand
- numbers out of range on a non-vprintf-style format, we won't have reached
- here. */
-
-static void
-finish_dollar_format_checking (status, res)
- int *status;
- format_check_results *res;
-{
- int i;
- for (i = 0; i < dollar_max_arg_used; i++)
- {
- if (!dollar_arguments_used[i])
- status_warning (status, "format argument %d unused before used argument %d in $-style format",
- i + 1, dollar_max_arg_used);
- }
- if (dollar_first_arg_num && dollar_max_arg_used < dollar_arguments_count)
- {
- res->number_other--;
- res->number_dollar_extra_args++;
- }
-}
-
-
-/* Retrieve the specification for a format flag. SPEC contains the
- specifications for format flags for the applicable kind of format.
- FLAG is the flag in question. If PREDICATES is NULL, the basic
- spec for that flag must be retrieved and this function aborts if
- it cannot be found. If PREDICATES is not NULL, it is a string listing
- possible predicates for the spec entry; if an entry predicated on any
- of these is found, it is returned, otherwise NULL is returned. */
-
-static const format_flag_spec *
-get_flag_spec (spec, flag, predicates)
- const format_flag_spec *spec;
- int flag;
- const char *predicates;
-{
- int i;
- for (i = 0; spec[i].flag_char != 0; i++)
- {
- if (spec[i].flag_char != flag)
- continue;
- if (predicates != NULL)
- {
- if (spec[i].predicate != 0
- && strchr (predicates, spec[i].predicate) != 0)
- return &spec[i];
- }
- else if (spec[i].predicate == 0)
- return &spec[i];
- }
- if (predicates == NULL)
- abort ();
- else
- return NULL;
-}
-
-
-/* Check the argument list of a call to printf, scanf, etc.
- INFO points to the function_format_info structure.
- PARAMS is the list of argument values. */
-
-static void
-check_format_info (status, info, params)
- int *status;
- function_format_info *info;
- tree params;
-{
- int arg_num;
- tree format_tree;
- format_check_results res;
- /* Skip to format argument. If the argument isn't available, there's
- no work for us to do; prototype checking will catch the problem. */
- for (arg_num = 1; ; ++arg_num)
- {
- if (params == 0)
- return;
- if (arg_num == info->format_num)
- break;
- params = TREE_CHAIN (params);
- }
- format_tree = TREE_VALUE (params);
- params = TREE_CHAIN (params);
- if (format_tree == 0)
- return;
-
- 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 > 1)
- 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)
- status_warning (status, "too many arguments for format");
- if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0
- && res.number_other == 0)
- status_warning (status, "unused arguments in $-style format");
- if (res.number_empty > 0 && res.number_non_literal == 0
- && res.number_other == 0)
- status_warning (status, "zero-length format string");
-
- if (res.number_wide > 0)
- status_warning (status, "format is a wide character string");
-
- if (res.number_unterminated > 0)
- status_warning (status, "unterminated format string");
-}
-
-
-/* Recursively check a call to a format function. FORMAT_TREE is the
- format parameter, which may be a conditional expression in which
- both halves should be checked. ARG_NUM is the number of the
- format argument; PARAMS points just after it in the argument list. */
-
-static void
-check_format_info_recurse (status, res, info, format_tree, params, arg_num)
- int *status;
- format_check_results *res;
- function_format_info *info;
- tree format_tree;
- tree params;
- int arg_num;
-{
- int format_length;
- const char *format_chars;
- tree array_size = 0;
- tree array_init;
-
- if (TREE_CODE (format_tree) == NOP_EXPR)
- {
- /* Strip coercion. */
- check_format_info_recurse (status, res, info,
- TREE_OPERAND (format_tree, 0), params,
- arg_num);
- return;
- }
-
- if (TREE_CODE (format_tree) == CALL_EXPR
- && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR
- && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0))
- == FUNCTION_DECL))
- {
- tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0);
-
- /* See if this is a call to a known internationalization function
- that modifies the format arg. */
- international_format_info *iinfo;
-
- for (iinfo = international_format_list; iinfo; iinfo = iinfo->next)
- if (iinfo->assembler_name
- ? (iinfo->assembler_name == DECL_ASSEMBLER_NAME (function))
- : (iinfo->name == DECL_NAME (function)))
- {
- tree inner_args;
- int i;
-
- for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
- inner_args != 0;
- inner_args = TREE_CHAIN (inner_args), i++)
- if (i == iinfo->format_num)
- {
- /* 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))
- {
- /* 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)
- {
- 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)
- {
- 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 (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)
- {
- res->number_unterminated++;
- return;
- }
-
- /* Skip to first argument to check. */
- while (arg_num + 1 < info->first_arg_num)
- {
- if (params == 0)
- return;
- params = TREE_CHAIN (params);
- ++arg_num;
- }
- /* Provisionally increment res->number_other; check_format_info_main
- will decrement it if it finds there are extra arguments, but this way
- need not adjust it for every return. */
- res->number_other++;
- check_format_info_main (status, res, info, format_chars, format_length,
- params, arg_num);
-}
-
-
-/* Do the main part of checking a call to a format function. FORMAT_CHARS
- is the NUL-terminated format string (which at this point may contain
- internal NUL characters); FORMAT_LENGTH is its length (excluding the
- terminating NUL character). ARG_NUM is one less than the number of
- the first format argument to check; PARAMS points to that format
- argument in the list of arguments. */
-
-static void
-check_format_info_main (status, res, info, format_chars, format_length,
- params, arg_num)
- int *status;
- format_check_results *res;
- function_format_info *info;
- const char *format_chars;
- int format_length;
- tree params;
- int arg_num;
-{
- const char *orig_format_chars = format_chars;
- tree first_fillin_param = params;
-
- const format_kind_info *fki = &format_types[info->format_type];
- const format_flag_spec *flag_specs = fki->flag_specs;
- const format_flag_pair *bad_flag_pairs = fki->bad_flag_pairs;
-
- /* -1 if no conversions taking an operand have been found; 0 if one has
- and it didn't use $; 1 if $ formats are in use. */
- int has_operand_number = -1;
-
- init_dollar_format_checking (info->first_arg_num, first_fillin_param);
-
- while (1)
- {
- 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 - orig_format_chars != format_length)
- status_warning (status, "embedded `\\0' in format");
- if (info->first_arg_num != 0 && params != 0
- && has_operand_number <= 0)
- {
- res->number_other--;
- res->number_extra_args++;
- }
- if (has_operand_number > 0)
- finish_dollar_format_checking (status, res);
- return;
- }
- if (*format_chars++ != '%')
- continue;
- if (*format_chars == 0)
- {
- status_warning (status, "spurious trailing `%%' in format");
- continue;
- }
- if (*format_chars == '%')
- {
- ++format_chars;
- continue;
- }
- flag_chars[0] = 0;
-
- if ((fki->flags & FMT_FLAG_ARG_CONVERT) && has_operand_number != 0)
- {
- /* Possibly read a $ operand number at the start of the format.
- If one was previously used, one is required here. If one
- is not used here, we can't immediately conclude this is a
- format without them, since it could be printf %m or scanf %*. */
- int opnum;
- opnum = maybe_read_dollar_number (status, &format_chars, 0,
- first_fillin_param,
- &main_arg_params, fki);
- if (opnum == -1)
- return;
- else if (opnum > 0)
- {
- has_operand_number = 1;
- main_arg_num = opnum + info->first_arg_num - 1;
- }
- }
-
- /* 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)
- {
- 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;
- }
-
- /* Read any format width, possibly * or *m$. */
- if (fki->width_char != 0)
- {
- if (fki->width_type != NULL && *format_chars == '*')
- {
- 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)
- {
- status_warning (status, "too few arguments for format");
- return;
- }
- if (has_operand_number != 0)
- {
- int opnum;
- opnum = maybe_read_dollar_number (status, &format_chars,
- has_operand_number == 1,
- first_fillin_param,
- ¶ms, fki);
- if (opnum == -1)
- return;
- else if (opnum > 0)
- {
- has_operand_number = 1;
- arg_num = opnum + info->first_arg_num - 1;
- }
- else
- has_operand_number = 0;
- }
- if (info->first_arg_num != 0)
- {
- cur_param = TREE_VALUE (params);
- if (has_operand_number <= 0)
- {
- params = TREE_CHAIN (params);
- ++arg_num;
- }
- width_wanted_type.wanted_type = *fki->width_type;
- width_wanted_type.wanted_type_name = NULL;
- width_wanted_type.pointer_count = 0;
- width_wanted_type.char_lenient_flag = 0;
- width_wanted_type.writing_in_flag = 0;
- width_wanted_type.name = _("field width");
- width_wanted_type.param = cur_param;
- width_wanted_type.arg_num = arg_num;
- width_wanted_type.next = NULL;
- if (last_wanted_type != 0)
- last_wanted_type->next = &width_wanted_type;
- if (first_wanted_type == 0)
- first_wanted_type = &width_wanted_type;
- last_wanted_type = &width_wanted_type;
- }
- }
- else
- {
- /* Possibly read a numeric width. If the width is zero,
- we complain; for scanf this is bad according to the
- standard, and for printf and strftime it cannot occur
- because 0 is a flag. */
- int non_zero_width_char = FALSE;
- int found_width = FALSE;
- while (ISDIGIT (*format_chars))
- {
- found_width = TRUE;
- if (*format_chars != '0')
- non_zero_width_char = TRUE;
- ++format_chars;
- }
- if (found_width && !non_zero_width_char)
- status_warning (status, "zero width in %s format",
- fki->name);
- if (found_width)
- {
- i = strlen (flag_chars);
- flag_chars[i++] = fki->width_char;
- flag_chars[i] = 0;
- }
- }
- }
-
- /* 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." */
- ++format_chars;
- if (has_operand_number != 0)
- {
- int opnum;
- opnum = maybe_read_dollar_number (status, &format_chars,
- has_operand_number == 1,
- first_fillin_param,
- ¶ms, fki);
- if (opnum == -1)
- return;
- else if (opnum > 0)
- {
- has_operand_number = 1;
- arg_num = opnum + info->first_arg_num - 1;
- }
- else
- has_operand_number = 0;
- }
- if (info->first_arg_num != 0)
- {
- 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;
- }
- }
-
- /* 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)
- fli++;
- if (fli->name != 0)
- {
- format_chars++;
- if (fli->double_name != 0 && fli->name[0] == *format_chars)
- {
- format_chars++;
- length_chars = fli->double_name;
- length_chars_val = fli->double_index;
- length_chars_std = fli->double_std;
- }
- else
- {
- length_chars = fli->name;
- length_chars_val = fli->index;
- length_chars_std = fli->std;
- }
- i = strlen (flag_chars);
- flag_chars[i++] = fki->length_code_char;
- flag_chars[i] = 0;
- }
- if (pedantic)
- {
- /* Warn if the length modifier is non-standard. */
- if (length_chars_std > C_STD_VER)
- status_warning (status, "%s does not support the `%s' %s length modifier",
- C_STD_NAME (length_chars_std), length_chars,
- fki->name);
- }
- }
-
- /* 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;
- }
- }
-
- /* 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. */
- i = strlen (flag_chars);
- flag_chars[i++] = 'a';
- flag_chars[i] = 0;
- format_chars++;
- }
- }
- }
-
- format_char = *format_chars;
- if (format_char == 0
- || (!(fki->flags & FMT_FLAG_FANCY_PERCENT_OK) && format_char == '%'))
- {
- status_warning (status, "conversion lacks type at end of format");
- continue;
- }
- format_chars++;
- fci = fki->conversion_specs;
- while (fci->format_chars != 0
- && strchr (fci->format_chars, format_char) == 0)
- ++fci;
- if (fci->format_chars == 0)
- {
- if (ISGRAPH(format_char))
- status_warning (status, "unknown conversion type character `%c' in format",
- format_char);
- else
- status_warning (status, "unknown conversion type character 0x%x in format",
- format_char);
- continue;
- }
- if (pedantic)
- {
- if (fci->std > C_STD_VER)
- status_warning (status, "%s does not support the `%%%c' %s format",
- C_STD_NAME (fci->std), format_char, fki->name);
- }
-
- /* Validate the individual flags used, removing any that are invalid. */
- {
- int d = 0;
- for (i = 0; flag_chars[i] != 0; i++)
- {
- const format_flag_spec *s = get_flag_spec (flag_specs,
- flag_chars[i], NULL);
- flag_chars[i - d] = flag_chars[i];
- if (flag_chars[i] == fki->length_code_char)
- continue;
- if (strchr (fci->flag_chars, flag_chars[i]) == 0)
- {
- status_warning (status, "%s used with `%%%c' %s format",
- _(s->name), format_char, fki->name);
- d++;
- continue;
- }
- if (pedantic)
- {
- const format_flag_spec *t;
- if (s->std > C_STD_VER)
- status_warning (status, "%s does not support %s",
- C_STD_NAME (s->std), _(s->long_name));
- t = get_flag_spec (flag_specs, flag_chars[i], fci->flags2);
- if (t != NULL && t->std > s->std)
- {
- const char *long_name = (t->long_name != NULL
- ? t->long_name
- : s->long_name);
- if (t->std > C_STD_VER)
- status_warning (status, "%s does not support %s with the `%%%c' %s format",
- C_STD_NAME (t->std), _(long_name),
- format_char, fki->name);
- }
- }
- }
- flag_chars[i - d] = 0;
- }
-
- if ((fki->flags & FMT_FLAG_SCANF_A_KLUDGE)
- && strchr (flag_chars, 'a') != 0)
- aflag = 1;
-
- if (fki->suppression_char
- && strchr (flag_chars, fki->suppression_char) != 0)
- suppressed = 1;
-
- /* Validate the pairs of flags used. */
- for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
- {
- const format_flag_spec *s, *t;
- if (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0)
- continue;
- if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0)
- continue;
- if (bad_flag_pairs[i].predicate != 0
- && strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0)
- continue;
- s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL);
- t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL);
- if (bad_flag_pairs[i].ignored)
- {
- if (bad_flag_pairs[i].predicate != 0)
- status_warning (status, "%s ignored with %s and `%%%c' %s format",
- _(s->name), _(t->name), format_char,
- fki->name);
- else
- status_warning (status, "%s ignored with %s in %s format",
- _(s->name), _(t->name), fki->name);
- }
- else
- {
- if (bad_flag_pairs[i].predicate != 0)
- status_warning (status, "use of %s and %s together with `%%%c' %s format",
- _(s->name), _(t->name), format_char,
- fki->name);
- else
- status_warning (status, "use of %s and %s together in %s format",
- _(s->name), _(t->name), fki->name);
- }
- }
-
- /* Give Y2K warnings. */
- {
- 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);
- }
-
- if (strchr (fci->flags2, '[') != 0)
- {
- /* Skip over scan set, in case it happens to have '%' in it. */
- if (*format_chars == '^')
- ++format_chars;
- /* Find closing bracket; if one is hit immediately, then
- it's part of the scan set rather than a terminator. */
- if (*format_chars == ']')
- ++format_chars;
- while (*format_chars && *format_chars != ']')
- ++format_chars;
- if (*format_chars != ']')
- /* The end of the format string was reached. */
- status_warning (status, "no closing `]' for `%%[' format");
- }
-
- wanted_type = 0;
- wanted_type_name = 0;
- if (fki->flags & FMT_FLAG_ARG_CONVERT)
- {
- wanted_type = (fci->types[length_chars_val].type
- ? *fci->types[length_chars_val].type : 0);
- wanted_type_name = fci->types[length_chars_val].name;
- wanted_type_std = fci->types[length_chars_val].std;
- if (wanted_type == 0)
- {
- status_warning (status, "use of `%s' length modifier with `%c' type character",
- length_chars, format_char);
- /* Heuristic: skip one argument when an invalid length/type
- combination is encountered. */
- arg_num++;
- if (params == 0)
- {
- status_warning (status, "too few arguments for format");
- return;
- }
- params = TREE_CHAIN (params);
- continue;
- }
- else if (pedantic
- /* Warn if non-standard, provided it is more non-standard
- than the length and type characters that may already
- have been warned for. */
- && wanted_type_std > length_chars_std
- && wanted_type_std > fci->std)
- {
- if (wanted_type_std > C_STD_VER)
- status_warning (status, "%s does not support the `%%%s%c' %s format",
- C_STD_NAME (wanted_type_std), length_chars,
- format_char, fki->name);
- }
- }
-
- /* Finally. . .check type of argument against desired type! */
- if (info->first_arg_num == 0)
- continue;
- if ((fci->pointer_count == 0 && wanted_type == void_type_node)
- || suppressed)
- {
- if (main_arg_num != 0)
- {
- if (suppressed)
- status_warning (status, "operand number specified with suppressed assignment");
- else
- status_warning (status, "operand number specified for format taking no argument");
- }
- }
- 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);
- main_wanted_type.wanted_type = wanted_type;
- main_wanted_type.wanted_type_name = wanted_type_name;
- main_wanted_type.pointer_count = fci->pointer_count + aflag;
- main_wanted_type.char_lenient_flag = 0;
- if (strchr (fci->flags2, 'c') != 0)
- main_wanted_type.char_lenient_flag = 1;
- main_wanted_type.writing_in_flag = 0;
- if (strchr (fci->flags2, 'W') != 0)
- main_wanted_type.writing_in_flag = 1;
- main_wanted_type.name = NULL;
- main_wanted_type.param = cur_param;
- main_wanted_type.arg_num = arg_num;
- main_wanted_type.next = NULL;
- if (last_wanted_type != 0)
- last_wanted_type->next = &main_wanted_type;
- if (first_wanted_type == 0)
- first_wanted_type = &main_wanted_type;
- last_wanted_type = &main_wanted_type;
- }
-
- if (first_wanted_type != 0)
- check_format_types (status, first_wanted_type);
-
- }
-}
-
-
-/* Check the argument types from a single format conversion (possibly
- including width and precision arguments). */
-static void
-check_format_types (status, types)
- int *status;
- format_wanted_type *types;
-{
- for (; types != 0; types = types->next)
- {
- tree cur_param;
- tree cur_type;
- tree orig_cur_type;
- tree wanted_type;
- tree promoted_type;
- int arg_num;
- int i;
- int char_type_flag;
- cur_param = types->param;
- cur_type = TREE_TYPE (cur_param);
- if (cur_type == error_mark_node)
- continue;
- char_type_flag = 0;
- wanted_type = types->wanted_type;
- arg_num = types->arg_num;
-
- /* The following should not occur here. */
- if (wanted_type == 0)
- 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
- that precede the "real" argument. */
- for (i = 0; i < types->pointer_count; ++i)
- {
- if (TREE_CODE (cur_type) == POINTER_TYPE)
- {
- cur_type = TREE_TYPE (cur_type);
- if (cur_type == error_mark_node)
- break;
-
- /* Check for writing through a NULL pointer. */
- if (types->writing_in_flag
- && i == 0
- && cur_param != 0
- && integer_zerop (cur_param))
- status_warning (status,
- "writing through null pointer (arg %d)",
- arg_num);
-
- if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR)
- cur_param = TREE_OPERAND (cur_param, 0);
- else
- cur_param = 0;
-
- /* See if this is an attempt to write into a const type with
- scanf or with printf "%n". Note: the writing in happens
- at the first indirection only, if for example
- void * const * is passed to scanf %p; passing
- const void ** is simply passing an incompatible type. */
- if (types->writing_in_flag
- && i == 0
- && (TYPE_READONLY (cur_type)
- || (cur_param != 0
- && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
- || (DECL_P (cur_param)
- && TREE_READONLY (cur_param))))))
- status_warning (status, "writing into constant object (arg %d)", arg_num);
-
- /* If there are extra type qualifiers beyond the first
- indirection, then this makes the types technically
- incompatible. */
- if (i > 0
- && pedantic
- && (TYPE_READONLY (cur_type)
- || TYPE_VOLATILE (cur_type)
- || TYPE_RESTRICT (cur_type)))
- status_warning (status, "extra type qualifiers in format argument (arg %d)",
- arg_num);
-
- }
- else
- {
- if (types->pointer_count == 1)
- status_warning (status, "format argument is not a pointer (arg %d)", arg_num);
- else
- status_warning (status, "format argument is not a pointer to a pointer (arg %d)", arg_num);
- 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 (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 (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 (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 (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)
- 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);
- }
- }
- }
-}
-\f
-/* Print a warning if a constant expression had overflow in folding.
- Invoke this function on every expression that the language
- requires to be a constant expression.
- Note the ANSI C standard says it is erroneous for a
- constant expression to overflow. */
-
-void
-constant_expression_warning (value)
- tree value;
-{
- if ((TREE_CODE (value) == INTEGER_CST || TREE_CODE (value) == REAL_CST
- || TREE_CODE (value) == COMPLEX_CST)
- && TREE_CONSTANT_OVERFLOW (value) && pedantic)
- pedwarn ("overflow in constant expression");
-}
-
-/* Print a warning if an expression had overflow in folding.
- Invoke this function on every expression that
- (1) appears in the source code, and
- (2) might be a constant expression that overflowed, and
- (3) is not already checked by convert_and_check;
- however, do not invoke this function on operands of explicit casts. */
-
-void
-overflow_warning (value)
- tree value;
-{
- if ((TREE_CODE (value) == INTEGER_CST
- || (TREE_CODE (value) == COMPLEX_CST
- && TREE_CODE (TREE_REALPART (value)) == INTEGER_CST))
- && TREE_OVERFLOW (value))
- {
- TREE_OVERFLOW (value) = 0;
- if (skip_evaluation == 0)
- warning ("integer overflow in expression");
- }
- else if ((TREE_CODE (value) == REAL_CST
- || (TREE_CODE (value) == COMPLEX_CST
- && TREE_CODE (TREE_REALPART (value)) == REAL_CST))
- && TREE_OVERFLOW (value))
- {
- TREE_OVERFLOW (value) = 0;
- if (skip_evaluation == 0)
- warning ("floating point overflow in expression");
- }
-}
-
-/* Print a warning if a large constant is truncated to unsigned,
- or if -Wconversion is used and a constant < 0 is converted to unsigned.
- Invoke this function on every expression that might be implicitly
- converted to an unsigned type. */
-
-void
-unsigned_conversion_warning (result, operand)
- tree result, operand;
-{
- if (TREE_CODE (operand) == INTEGER_CST
- && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
- && TREE_UNSIGNED (TREE_TYPE (result))
- && skip_evaluation == 0
- && !int_fits_type_p (operand, TREE_TYPE (result)))
- {
- if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
- /* This detects cases like converting -129 or 256 to unsigned char. */
- warning ("large integer implicitly truncated to unsigned type");
- else if (warn_conversion)
- warning ("negative integer implicitly converted to unsigned type");
- }
-}
+static int
+constant_fits_type_p (c, type)
+ tree c, type;
+{
+ if (TREE_CODE (c) == INTEGER_CST)
+ return int_fits_type_p (c, type);
+
+ c = convert (type, c);
+ return !TREE_OVERFLOW (c);
+}
/* Convert EXPR to TYPE, warning about conversion problems with constants.
Invoke this function on every expression that is converted implicitly,
don't warn unless pedantic. */
if ((pedantic
|| TREE_UNSIGNED (type)
- || ! int_fits_type_p (expr, unsigned_type (type)))
+ || ! constant_fits_type_p (expr, unsigned_type (type)))
&& skip_evaluation == 0)
warning ("overflow in implicit constant conversion");
}
return t;
}
\f
-/* Describe a reversed version of a normal tree, so that we can get to the
- parent of each node. */
-struct reverse_tree
+/* 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
{
- /* All reverse_tree structures for a given tree are chained through this
- field. */
- struct reverse_tree *next;
- /* The parent of this node. */
- struct reverse_tree *parent;
- /* The actual tree node. */
- tree x;
- /* The operand number this node corresponds to in the parent. */
- int operandno;
- /* Describe whether this expression is written to or read. */
- char read, write;
+ struct tlist_cache *next;
+ struct tlist *cache_before_sp;
+ struct tlist *cache_after_sp;
+ tree expr;
};
-/* A list of all reverse_tree structures for a given expression, built by
- build_reverse_tree. */
-static struct reverse_tree *reverse_list;
-/* The maximum depth of a tree, computed by build_reverse_tree. */
-static int reverse_max_depth;
-
-static void build_reverse_tree PARAMS ((tree, struct reverse_tree *, int, int,
- int, int));
-static struct reverse_tree *common_ancestor PARAMS ((struct reverse_tree *,
- struct reverse_tree *,
- struct reverse_tree **,
- struct reverse_tree **));
-static int modify_ok PARAMS ((struct reverse_tree *, struct reverse_tree *));
+/* 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));
-/* Recursively process an expression, X, building a reverse tree while
- descending and recording OPERANDNO, READ, and WRITE in the created
- structures. DEPTH is used to compute reverse_max_depth.
- FIXME: if walk_tree gets moved out of the C++ front end, this should
- probably use walk_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 point
+ 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
-build_reverse_tree (x, parent, operandno, read, write, depth)
+verify_tree (x, pbefore_sp, pno_sp, writer)
tree x;
- struct reverse_tree *parent;
- int operandno, read, write, depth;
+ struct tlist **pbefore_sp, **pno_sp;
+ tree writer;
{
- struct reverse_tree *node;
+ struct tlist *tmp_before, *tmp_nosp, *tmp_list2, *tmp_list3;
+ enum tree_code code;
+ char class;
- if (x == 0 || x == error_mark_node)
+ /* X may be NULL if it is the operand of an empty statement expression
+ ({ }). */
+ if (x == NULL)
return;
- node = (struct reverse_tree *) xmalloc (sizeof (struct reverse_tree));
+ restart:
+ code = TREE_CODE (x);
+ class = TREE_CODE_CLASS (code);
- node->parent = parent;
- node->x = x;
- node->read = read;
- node->write = write;
- node->operandno = operandno;
- node->next = reverse_list;
- reverse_list = node;
- if (depth > reverse_max_depth)
- reverse_max_depth = depth;
+ if (warning_candidate_p (x))
+ {
+ *pno_sp = new_tlist (*pno_sp, x, writer);
+ return;
+ }
- switch (TREE_CODE (x))
+ 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:
- build_reverse_tree (TREE_OPERAND (x, 0), node, 0, 1, 1, depth + 1);
- break;
+ 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:
- build_reverse_tree (TREE_OPERAND (x, 0), node, 0, 1, 0, depth + 1);
- x = TREE_OPERAND (x, 1);
- while (x)
- {
- build_reverse_tree (TREE_VALUE (x), node, 1, 1, 0, depth + 1);
- x = TREE_CHAIN (x);
- }
- break;
+ /* 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)
{
- build_reverse_tree (TREE_VALUE (x), node, 0, 1, 0, depth + 1);
+ 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);
}
- break;
+ return;
- case MODIFY_EXPR:
- build_reverse_tree (TREE_OPERAND (x, 0), node, 0, 0, 1, depth + 1);
- build_reverse_tree (TREE_OPERAND (x, 1), node, 1, 1, 0, depth + 1);
- break;
+ case SAVE_EXPR:
+ {
+ struct tlist_cache *t;
+ for (t = save_expr_cache; t; t = t->next)
+ if (t->expr == x)
+ break;
- default:
- switch (TREE_CODE_CLASS (TREE_CODE (x)))
- {
- case 'r':
- case '<':
- case '2':
- case 'b':
- case '1':
- case 'e':
- case 's':
- case 'x':
+ if (! t)
{
- int lp;
- int max = first_rtl_op (TREE_CODE (x));
- for (lp = 0; lp < max; lp++)
- build_reverse_tree (TREE_OPERAND (x, lp), node, lp, 1, 0,
- depth + 1);
- break;
+ 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;
}
- default:
- break;
- }
+ merge_tlist (pbefore_sp, t->cache_before_sp, 1);
+ add_tlist (pno_sp, t->cache_after_sp, NULL_TREE, 1);
+ return;
+ }
+ default:
break;
}
-}
-
-/* Given nodes P1 and P2 as well as enough scratch space pointed to by TMP1
- and TMP2, find the common ancestor of P1 and P2. */
-static struct reverse_tree *
-common_ancestor (p1, p2, tmp1, tmp2)
- struct reverse_tree *p1, *p2;
- struct reverse_tree **tmp1, **tmp2;
-{
- struct reverse_tree *t1 = p1;
- struct reverse_tree *t2 = p2;
- int i, j;
-
- /* First, check if we're actually looking at the same expression twice,
- which can happen if it's wrapped in a SAVE_EXPR - in this case there's
- no chance of conflict. */
- while (t1 && t2 && t1->x == t2->x)
+ if (class == '1')
{
- if (TREE_CODE (t1->x) == SAVE_EXPR)
- return 0;
- t1 = t1->parent;
- t2 = t2->parent;
+ if (first_rtl_op (code) == 0)
+ return;
+ x = TREE_OPERAND (x, 0);
+ writer = 0;
+ goto restart;
}
- for (i = 0; p1; i++, p1 = p1->parent)
- tmp1[i] = p1;
- for (j = 0; p2; j++, p2 = p2->parent)
- tmp2[j] = p2;
- while (tmp1[i - 1] == tmp2[j - 1])
- i--, j--;
-
- return tmp1[i];
-}
-
-/* Subroutine of verify_sequence_points to check whether a node T corresponding
- to a MODIFY_EXPR invokes undefined behaviour. OTHER occurs somewhere in the
- RHS, and an identical expression is the LHS of T.
- For MODIFY_EXPRs, some special cases apply when testing for undefined
- behaviour if one of the expressions we found is the LHS of the MODIFY_EXPR.
- If the other expression is just a use, then there's no undefined behaviour.
- Likewise, if the other expression is wrapped inside another expression that
- will force a sequence point, then there's no undefined behaviour either. */
-
-static int
-modify_ok (t, other)
- struct reverse_tree *t, *other;
-{
- struct reverse_tree *p;
-
- if (! other->write)
- return 1;
-
- /* See if there's an intervening sequence point. */
- for (p = other; p->parent != t; p = p->parent)
+ switch (class)
{
- if ((TREE_CODE (p->parent->x) == COMPOUND_EXPR
- || TREE_CODE (p->parent->x) == TRUTH_ANDIF_EXPR
- || TREE_CODE (p->parent->x) == TRUTH_ORIF_EXPR
- || TREE_CODE (p->parent->x) == COND_EXPR)
- && p->operandno == 0)
- return 1;
- if (TREE_CODE (p->parent->x) == SAVE_EXPR)
- return 1;
- if (TREE_CODE (p->parent->x) == CALL_EXPR
- && p->operandno != 0)
- return 1;
+ 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;
+ }
}
- return 0;
}
/* Try to warn for undefined behaviour in EXPR due to missing sequence
verify_sequence_points (expr)
tree expr;
{
- struct reverse_tree **tmp1, **tmp2;
- struct reverse_tree *p;
-
- reverse_list = 0;
- reverse_max_depth = 0;
- build_reverse_tree (expr, NULL, 0, 1, 0, 1);
-
- tmp1 = (struct reverse_tree **) xmalloc (sizeof (struct reverse_tree *)
- * reverse_max_depth);
- tmp2 = (struct reverse_tree **) xmalloc (sizeof (struct reverse_tree *)
- * reverse_max_depth);
-
- /* Search for multiple occurrences of the same variable, where either both
- occurrences are writes, or one is a read and a write. If we can't prove
- that these are ordered by a sequence point, warn that the expression is
- undefined. */
- for (p = reverse_list; p; p = p->next)
- {
- struct reverse_tree *p2;
- if (TREE_CODE (p->x) != VAR_DECL && TREE_CODE (p->x) != PARM_DECL)
- continue;
- for (p2 = p->next; p2; p2 = p2->next)
- {
- if ((TREE_CODE (p2->x) == VAR_DECL || TREE_CODE (p2->x) == PARM_DECL)
- && DECL_NAME (p->x) == DECL_NAME (p2->x)
- && (p->write || p2->write))
- {
- struct reverse_tree *t = common_ancestor (p, p2, tmp1, tmp2);
-
- if (t == 0
- || TREE_CODE (t->x) == COMPOUND_EXPR
- || TREE_CODE (t->x) == TRUTH_ANDIF_EXPR
- || TREE_CODE (t->x) == TRUTH_ORIF_EXPR
- || TREE_CODE (t->x) == COND_EXPR)
- continue;
- if (TREE_CODE (t->x) == MODIFY_EXPR
- && p->parent == t
- && modify_ok (t, p2))
- continue;
- if (TREE_CODE (t->x) == MODIFY_EXPR
- && p2->parent == t
- && modify_ok (t, p))
- continue;
-
- warning ("operation on `%s' may be undefined",
- IDENTIFIER_POINTER (DECL_NAME (p->x)));
- break;
- }
- }
- }
+ struct tlist *before_sp = 0, *after_sp = 0;
- while (reverse_list)
+ warned_ids = 0;
+ save_expr_cache = 0;
+ if (tlist_firstobj == 0)
{
- struct reverse_tree *p = reverse_list;
- reverse_list = p->next;
- free (p);
+ gcc_obstack_init (&tlist_obstack);
+ tlist_firstobj = obstack_alloc (&tlist_obstack, 0);
}
- free (tmp1);
- free (tmp2);
+
+ verify_tree (expr, &before_sp, &after_sp, 0);
+ warn_for_collisions (after_sp);
+ obstack_free (&tlist_obstack, tlist_firstobj);
}
-void
+tree
c_expand_expr_stmt (expr)
tree expr;
{
/* Do default conversion if safe and possibly important,
in case within ({...}). */
- if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && lvalue_p (expr))
+ if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
+ && (flag_isoc99 || lvalue_p (expr)))
|| TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
expr = default_conversion (expr);
error ("expression statement has incomplete type");
last_expr_type = TREE_TYPE (expr);
- add_stmt (build_stmt (EXPR_STMT, expr));
+ return add_stmt (build_stmt (EXPR_STMT, expr));
}
\f
/* Validate the expression after `case' and apply default promotions. */
return unsignedp ? widest_unsigned_literal_type_node
: widest_integer_literal_type_node;
- if (mode == TYPE_MODE (intQI_type_node))
+ if (mode == QImode)
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
- if (mode == TYPE_MODE (intHI_type_node))
+ if (mode == HImode)
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
- if (mode == TYPE_MODE (intSI_type_node))
+ if (mode == SImode)
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
- if (mode == TYPE_MODE (intDI_type_node))
+ if (mode == DImode)
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
#if HOST_BITS_PER_WIDE_INT >= 64
return build_pointer_type (integer_type_node);
#ifdef VECTOR_MODE_SUPPORTED_P
- if (mode == TYPE_MODE (V4SF_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
- return V4SF_type_node;
- if (mode == TYPE_MODE (V4SI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
- return V4SI_type_node;
- if (mode == TYPE_MODE (V2SI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
- return V2SI_type_node;
- if (mode == TYPE_MODE (V4HI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
- return V4HI_type_node;
- if (mode == TYPE_MODE (V8QI_type_node) && VECTOR_MODE_SUPPORTED_P (mode))
- return V8QI_type_node;
+ if (VECTOR_MODE_SUPPORTED_P (mode))
+ {
+ switch (mode)
+ {
+ case V16QImode:
+ return unsignedp ? unsigned_V16QI_type_node : V16QI_type_node;
+ case V8HImode:
+ return unsignedp ? unsigned_V8HI_type_node : V8HI_type_node;
+ case V4SImode:
+ return unsignedp ? unsigned_V4SI_type_node : V4SI_type_node;
+ case V2SImode:
+ return unsignedp ? unsigned_V2SI_type_node : V2SI_type_node;
+ case V4HImode:
+ return unsignedp ? unsigned_V4HI_type_node : V4HI_type_node;
+ case V8QImode:
+ return unsignedp ? unsigned_V8QI_type_node : V8QI_type_node;
+ case V4SFmode:
+ return V4SF_type_node;
+ case V2SFmode:
+ return V2SF_type_node;
+ default:
+ break;
+ }
+ }
#endif
return 0;
}
-/* Return an unsigned type the same as TYPE in other respects. */
+/* Return an unsigned type the same as TYPE in other respects. */
tree
unsigned_type (type)
tree type;
if (TYPE_PRECISION (type) == TYPE_PRECISION (widest_integer_literal_type_node))
return (unsignedp ? widest_unsigned_literal_type_node
: widest_integer_literal_type_node);
+
+#if HOST_BITS_PER_WIDE_INT >= 64
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (intTI_type_node))
+ return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+#endif
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (intDI_type_node))
+ return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (intSI_type_node))
+ return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (intHI_type_node))
+ return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (intQI_type_node))
+ return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+
return type;
}
\f
binary_op_error (code)
enum tree_code code;
{
- register const char *opname;
+ const char *opname;
switch (code)
{
tree *restype_ptr;
enum tree_code *rescode_ptr;
{
- register tree type;
+ tree type;
tree op0 = *op0_ptr;
tree op1 = *op1_ptr;
int unsignedp0, unsignedp1;
if (TREE_CONSTANT (primop0)
&& ! integer_zerop (primop1) && ! real_zerop (primop1))
{
- register tree tem = primop0;
- register int temi = unsignedp0;
+ tree tem = primop0;
+ int temi = unsignedp0;
primop0 = primop1;
primop1 = tem;
tem = op0;
return real_zerop (expr) ? boolean_false_node : boolean_true_node;
case ADDR_EXPR:
- /* If we are taking the address of a external decl, it might be zero
+ /* If we are taking the address of an external decl, it might be zero
if it is weak, so we cannot optimize. */
if (DECL_P (TREE_OPERAND (expr, 0))
&& DECL_EXTERNAL (TREE_OPERAND (expr, 0)))
return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
}
\f
-#if !USE_CPPLIB
-/* Read the rest of a #-directive from input stream FINPUT.
- In normal use, the directive name and the white space after it
- have already been read, so they won't be included in the result.
- We allow for the fact that the directive line may contain
- a newline embedded within a character or string literal which forms
- a part of the directive.
-
- The value is a string in a reusable buffer. It remains valid
- only until the next time this function is called.
-
- 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). */
+static tree builtin_function_2 PARAMS ((const char *, const char *, tree, tree,
+ int, enum built_in_class, int, int,
+ int));
- /* Handle backslash. */
- char_escaped = (c == '\\' && ! char_escaped);
- }
-}
-#endif /* USE_CPPLIB */
-\f
/* Make a variant type in the proper way for C/C++, propagating qualifiers
down to the element type of an array. */
|| !C_TYPE_OBJECT_OR_INCOMPLETE_P (TREE_TYPE (TREE_TYPE (decl))))
error ("invalid use of `restrict'");
else if (flag_strict_aliasing)
- {
- /* No two restricted pointers can point at the same thing.
- However, a restricted pointer can point at the same thing
- as an unrestricted pointer, if that unrestricted pointer
- is based on the restricted pointer. So, we make the
- alias set for the restricted pointer a subset of the
- alias set for the type pointed to by the type of the
- decl. */
-
- HOST_WIDE_INT pointed_to_alias_set
- = get_alias_set (TREE_TYPE (TREE_TYPE (decl)));
-
- if (pointed_to_alias_set == 0)
- /* It's not legal to make a subset of alias set zero. */
- ;
- else
- {
- DECL_POINTER_ALIAS_SET (decl) = new_alias_set ();
- record_alias_subset (pointed_to_alias_set,
- DECL_POINTER_ALIAS_SET (decl));
- }
- }
+ /* Indicate we need to make a unique alias set for this pointer.
+ We can't do it here because it might be pointing to an
+ incomplete type. */
+ DECL_POINTER_ALIAS_SET (decl) = -2;
}
}
or a type. Return -1 if we don't do anything special. */
HOST_WIDE_INT
-lang_get_alias_set (t)
+c_common_get_alias_set (t)
tree t;
{
tree u;
-
+
+ /* We know nothing about vector types */
+ if (TREE_CODE (t) == VECTOR_TYPE)
+ return 0;
+
/* Permit type-punning when accessing a union, provided the access
is directly through the union. For example, this code does not
permit taking the address of a union member and then storing
Technically, this approach is actually more conservative that
it needs to be. In particular, `const int *' and `int *'
- chould be in different alias sets, according to the C and C++
+ should be in different alias sets, according to the C and C++
standard, since their types are not the same, and so,
technically, an `int **' and `const int **' cannot point at
the same thing.
int *ip;
int **ipp = &ip;
- const int* const* cipp = &ip;
+ const int* const* cipp = &ipp;
And, it doesn't make sense for that to be legal unless you
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);
}
- /* It's not yet safe to use alias sets for classes in C++ because
- the TYPE_FIELDs list for a class doesn't mention base classes. */
- else if (c_language == clk_cplusplus && AGGREGATE_TYPE_P (t))
- return 0;
- return -1;
-}
+ return -1;
+}
+\f
+/* Implement the __alignof keyword: Return the minimum required
+ alignment of TYPE, measured in bytes. */
+
+tree
+c_alignof (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ /* In C++, sizeof applies to the referent. Handle alignof the same way. */
+ if (code == REFERENCE_TYPE)
+ {
+ type = TREE_TYPE (type);
+ code = TREE_CODE (type);
+ }
+
+ if (code == FUNCTION_TYPE)
+ t = size_int (FUNCTION_BOUNDARY / BITS_PER_UNIT);
+ else if (code == VOID_TYPE || code == ERROR_MARK)
+ t = size_one_node;
+ else if (!COMPLETE_TYPE_P (type))
+ {
+ error ("__alignof__ applied to an incomplete type");
+ t = size_zero_node;
+ }
+ else
+ t = size_int (TYPE_ALIGN (type) / BITS_PER_UNIT);
+
+ return fold (build1 (NOP_EXPR, c_size_type_node, t));
+}
+
+/* Implement the __alignof keyword: Return the minimum required
+ alignment of EXPR, measured in bytes. For VAR_DECL's and
+ FIELD_DECL's return DECL_ALIGN (which can be set from an
+ "aligned" __attribute__ specification). */
+
+tree
+c_alignof_expr (expr)
+ tree expr;
+{
+ tree t;
+
+ if (TREE_CODE (expr) == VAR_DECL)
+ t = size_int (DECL_ALIGN (expr) / BITS_PER_UNIT);
+
+ else if (TREE_CODE (expr) == COMPONENT_REF
+ && DECL_C_BIT_FIELD (TREE_OPERAND (expr, 1)))
+ {
+ error ("`__alignof' applied to a bit-field");
+ t = size_one_node;
+ }
+ else if (TREE_CODE (expr) == COMPONENT_REF
+ && TREE_CODE (TREE_OPERAND (expr, 1)) == FIELD_DECL)
+ t = size_int (DECL_ALIGN (TREE_OPERAND (expr, 1)) / BITS_PER_UNIT);
+
+ else if (TREE_CODE (expr) == INDIRECT_REF)
+ {
+ tree t = TREE_OPERAND (expr, 0);
+ tree best = t;
+ int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
+
+ while (TREE_CODE (t) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
+ {
+ int thisalign;
+
+ t = TREE_OPERAND (t, 0);
+ thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
+ if (thisalign > bestalign)
+ best = t, bestalign = thisalign;
+ }
+ return c_alignof (TREE_TYPE (TREE_TYPE (best)));
+ }
+ else
+ return c_alignof (TREE_TYPE (expr));
+
+ return fold (build1 (NOP_EXPR, c_size_type_node, t));
+}
+\f
+/* Give the specifications for the format attributes, used by C and all
+ descendents. */
+
+static const struct attribute_spec c_format_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "format", 3, 3, false, true, true,
+ handle_format_attribute },
+ { "format_arg", 1, 1, false, true, true,
+ handle_format_arg_attribute },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+
+/* Build tree nodes and builtin functions common to both C and C++ language
+ frontends. */
+
+void
+c_common_nodes_and_builtins ()
+{
+ enum builtin_type
+ {
+#define DEF_PRIMITIVE_TYPE(NAME, VALUE) NAME,
+#define DEF_FUNCTION_TYPE_0(NAME, RETURN) NAME,
+#define DEF_FUNCTION_TYPE_1(NAME, RETURN, ARG1) NAME,
+#define DEF_FUNCTION_TYPE_2(NAME, RETURN, ARG1, ARG2) NAME,
+#define DEF_FUNCTION_TYPE_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME,
+#define DEF_FUNCTION_TYPE_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME,
+#define DEF_FUNCTION_TYPE_VAR_0(NAME, RETURN) NAME,
+#define DEF_FUNCTION_TYPE_VAR_1(NAME, RETURN, ARG1) NAME,
+#define DEF_FUNCTION_TYPE_VAR_2(NAME, RETURN, ARG1, ARG2) NAME,
+#define DEF_POINTER_TYPE(NAME, TYPE) NAME,
+#include "builtin-types.def"
+#undef DEF_PRIMITIVE_TYPE
+#undef DEF_FUNCTION_TYPE_0
+#undef DEF_FUNCTION_TYPE_1
+#undef DEF_FUNCTION_TYPE_2
+#undef DEF_FUNCTION_TYPE_3
+#undef DEF_FUNCTION_TYPE_4
+#undef DEF_FUNCTION_TYPE_VAR_0
+#undef DEF_FUNCTION_TYPE_VAR_1
+#undef DEF_FUNCTION_TYPE_VAR_2
+#undef DEF_POINTER_TYPE
+ BT_LAST
+ };
+
+ typedef enum builtin_type builtin_type;
+
+ tree builtin_types[(int)BT_LAST];
+ int wchar_type_size;
+ tree array_domain_type;
+ /* Either char* or void*. */
+ tree traditional_ptr_type_node;
+ /* Either const char* or const void*. */
+ tree traditional_cptr_type_node;
+ tree traditional_len_type_node;
+ tree va_list_ref_type_node;
+ tree va_list_arg_type_node;
+
+ /* We must initialize this before any builtin functions (which might have
+ attributes) are declared. (c_common_init is too late.) */
+ format_attribute_table = c_format_attribute_table;
+
+ /* Define `int' and `char' first so that dbx will output them first. */
+ record_builtin_type (RID_INT, NULL, integer_type_node);
+ record_builtin_type (RID_CHAR, "char", char_type_node);
+
+ /* `signed' is the same as `int'. FIXME: the declarations of "signed",
+ "unsigned long", "long long unsigned" and "unsigned short" were in C++
+ but not C. Are the conditionals here needed? */
+ if (c_language == clk_cplusplus)
+ record_builtin_type (RID_SIGNED, NULL, integer_type_node);
+ record_builtin_type (RID_LONG, "long int", long_integer_type_node);
+ record_builtin_type (RID_UNSIGNED, "unsigned int", unsigned_type_node);
+ record_builtin_type (RID_MAX, "long unsigned int",
+ long_unsigned_type_node);
+ if (c_language == clk_cplusplus)
+ record_builtin_type (RID_MAX, "unsigned long", long_unsigned_type_node);
+ record_builtin_type (RID_MAX, "long long int",
+ long_long_integer_type_node);
+ record_builtin_type (RID_MAX, "long long unsigned int",
+ long_long_unsigned_type_node);
+ if (c_language == clk_cplusplus)
+ record_builtin_type (RID_MAX, "long long unsigned",
+ long_long_unsigned_type_node);
+ record_builtin_type (RID_SHORT, "short int", short_integer_type_node);
+ record_builtin_type (RID_MAX, "short unsigned int",
+ short_unsigned_type_node);
+ if (c_language == clk_cplusplus)
+ record_builtin_type (RID_MAX, "unsigned short",
+ short_unsigned_type_node);
+
+ /* Define both `signed char' and `unsigned char'. */
+ record_builtin_type (RID_MAX, "signed char", signed_char_type_node);
+ record_builtin_type (RID_MAX, "unsigned char", unsigned_char_type_node);
+
+ /* These are types that type_for_size and type_for_mode use. */
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intQI_type_node));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intHI_type_node));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intSI_type_node));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intDI_type_node));
+#if HOST_BITS_PER_WIDE_INT >= 64
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("__int128_t"), intTI_type_node));
+#endif
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intQI_type_node));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intHI_type_node));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intSI_type_node));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intDI_type_node));
+#if HOST_BITS_PER_WIDE_INT >= 64
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("__uint128_t"), unsigned_intTI_type_node));
+#endif
+
+ /* Create the widest literal types. */
+ widest_integer_literal_type_node
+ = make_signed_type (HOST_BITS_PER_WIDE_INT * 2);
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE,
+ widest_integer_literal_type_node));
+
+ widest_unsigned_literal_type_node
+ = make_unsigned_type (HOST_BITS_PER_WIDE_INT * 2);
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE,
+ widest_unsigned_literal_type_node));
+
+ /* `unsigned long' is the standard type for sizeof.
+ Note that stddef.h uses `unsigned long',
+ and this must agree, even if long and int are the same size. */
+ c_size_type_node =
+ TREE_TYPE (identifier_global_value (get_identifier (SIZE_TYPE)));
+ signed_size_type_node = signed_type (c_size_type_node);
+ if (flag_traditional)
+ c_size_type_node = signed_size_type_node;
+ set_sizetype (c_size_type_node);
+
+ build_common_tree_nodes_2 (flag_short_double);
+
+ record_builtin_type (RID_FLOAT, NULL, float_type_node);
+ record_builtin_type (RID_DOUBLE, NULL, double_type_node);
+ record_builtin_type (RID_MAX, "long double", long_double_type_node);
+
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex int"),
+ complex_integer_type_node));
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex float"),
+ complex_float_type_node));
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex double"),
+ complex_double_type_node));
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex long double"),
+ complex_long_double_type_node));
+
+ record_builtin_type (RID_VOID, NULL, void_type_node);
+
+ void_zero_node = build_int_2 (0, 0);
+ TREE_TYPE (void_zero_node) = void_type_node;
+
+ void_list_node = build_void_list_node ();
+
+ /* Make a type to be the domain of a few array types
+ whose domains don't really matter.
+ 200 is small enough that it always fits in size_t
+ and large enough that it can hold most function names for the
+ initializations of __FUNCTION__ and __PRETTY_FUNCTION__. */
+ array_domain_type = build_index_type (size_int (200));
+
+ /* Make a type for arrays of characters.
+ With luck nothing will ever really depend on the length of this
+ array type. */
+ char_array_type_node
+ = build_array_type (char_type_node, array_domain_type);
+
+ /* Likewise for arrays of ints. */
+ int_array_type_node
+ = build_array_type (integer_type_node, array_domain_type);
+
+ string_type_node = build_pointer_type (char_type_node);
+ const_string_type_node
+ = build_pointer_type (build_qualified_type
+ (char_type_node, TYPE_QUAL_CONST));
+
+ traditional_ptr_type_node = ((flag_traditional &&
+ c_language != clk_cplusplus)
+ ? string_type_node : ptr_type_node);
+ traditional_cptr_type_node = ((flag_traditional &&
+ c_language != clk_cplusplus)
+ ? const_string_type_node : const_ptr_type_node);
+
+ (*targetm.init_builtins) ();
+
+ /* This is special for C++ so functions can be overloaded. */
+ wchar_type_node = get_identifier (flag_short_wchar
+ ? "short unsigned int"
+ : WCHAR_TYPE);
+ wchar_type_node = TREE_TYPE (identifier_global_value (wchar_type_node));
+ wchar_type_size = TYPE_PRECISION (wchar_type_node);
+ if (c_language == clk_cplusplus)
+ {
+ if (TREE_UNSIGNED (wchar_type_node))
+ wchar_type_node = make_unsigned_type (wchar_type_size);
+ else
+ wchar_type_node = make_signed_type (wchar_type_size);
+ record_builtin_type (RID_WCHAR, "wchar_t", wchar_type_node);
+ }
+ else
+ {
+ signed_wchar_type_node = signed_type (wchar_type_node);
+ unsigned_wchar_type_node = unsigned_type (wchar_type_node);
+ }
+
+ /* This is for wide string constants. */
+ wchar_array_type_node
+ = build_array_type (wchar_type_node, array_domain_type);
-/* Build tree nodes and builtin functions common to both C and C++ language
- frontends. */
+ wint_type_node =
+ TREE_TYPE (identifier_global_value (get_identifier (WINT_TYPE)));
-void
-c_common_nodes_and_builtins ()
-{
- tree temp;
- tree memcpy_ftype, memset_ftype, strlen_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, 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 long_ftype_long;
- tree longlong_ftype_longlong;
- /* Either char* or void*. */
- tree traditional_ptr_type_node;
- /* Either const char* or const void*. */
- tree traditional_cptr_type_node;
- tree traditional_len_type_node;
- tree traditional_len_endlink;
- tree va_list_ref_type_node;
- tree va_list_arg_type_node;
+ 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));
va_list_ref_type_node = build_reference_type (va_list_type_node);
}
- endlink = void_list_node;
- int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink);
- double_endlink = tree_cons (NULL_TREE, double_type_node, endlink);
- unsigned_endlink = tree_cons (NULL_TREE, unsigned_type_node, endlink);
-
- ptr_ftype = build_function_type (ptr_type_node, NULL_TREE);
- ptr_ftype_unsigned = build_function_type (ptr_type_node, unsigned_endlink);
- sizetype_endlink = tree_cons (NULL_TREE, TYPE_DOMAIN (sizetype), endlink);
- /* 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);
- void_ftype_int = build_function_type (void_type_node, int_endlink);
- void_ftype_ptr
- = build_function_type (void_type_node,
- tree_cons (NULL_TREE, ptr_type_node, endlink));
-
- float_ftype_float
- = build_function_type (float_type_node,
- tree_cons (NULL_TREE, float_type_node, endlink));
-
- double_ftype_double
- = build_function_type (double_type_node, double_endlink);
-
- ldouble_ftype_ldouble
- = build_function_type (long_double_type_node,
- tree_cons (NULL_TREE, long_double_type_node,
- endlink));
-
- double_ftype_double_double
- = build_function_type (double_type_node,
- tree_cons (NULL_TREE, double_type_node,
- double_endlink));
-
- int_ftype_int
- = build_function_type (integer_type_node, int_endlink);
-
- long_ftype_long
- = build_function_type (long_integer_type_node,
- 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));
-
- int_ftype_cptr_cptr_sizet
- = build_function_type (integer_type_node,
- tree_cons (NULL_TREE, const_ptr_type_node,
- tree_cons (NULL_TREE, const_ptr_type_node,
- tree_cons (NULL_TREE,
- sizetype,
- endlink))));
-
- void_zero_node = build_int_2 (0, 0);
- TREE_TYPE (void_zero_node) = void_type_node;
-
- /* Prototype for strcpy. */
- string_ftype_ptr_ptr
- = build_function_type (string_type_node,
- tree_cons (NULL_TREE, string_type_node,
- tree_cons (NULL_TREE,
- const_string_type_node,
- 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);
-
- /* Prototype for strcmp. */
- int_ftype_string_string
- = build_function_type (integer_type_node,
- tree_cons (NULL_TREE, const_string_type_node,
- tree_cons (NULL_TREE,
- const_string_type_node,
- 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 &&
- c_language != clk_cplusplus)
- ? string_type_node : ptr_type_node);
- traditional_cptr_type_node = ((flag_traditional &&
- c_language != clk_cplusplus)
- ? const_string_type_node : const_ptr_type_node);
- /* Prototype for memcpy. */
- memcpy_ftype
- = build_function_type (traditional_ptr_type_node,
- tree_cons (NULL_TREE, ptr_type_node,
- tree_cons (NULL_TREE, const_ptr_type_node,
- sizetype_endlink)));
-
- /* Prototype for memset. */
- memset_ftype
- = build_function_type (traditional_ptr_type_node,
- tree_cons (NULL_TREE, ptr_type_node,
- tree_cons (NULL_TREE, integer_type_node,
- tree_cons (NULL_TREE,
- sizetype,
- endlink))));
-
- /* Prototype for bzero. */
- bzero_ftype
- = build_function_type (void_type_node,
- tree_cons (NULL_TREE, traditional_ptr_type_node,
- traditional_len_endlink));
-
- /* Prototype for bcmp. */
- bcmp_ftype
- = build_function_type (integer_type_node,
- tree_cons (NULL_TREE, traditional_cptr_type_node,
- tree_cons (NULL_TREE,
- 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,
+#define DEF_PRIMITIVE_TYPE(ENUM, VALUE) \
+ builtin_types[(int) ENUM] = VALUE;
+#define DEF_FUNCTION_TYPE_0(ENUM, RETURN) \
+ builtin_types[(int) ENUM] \
+ = build_function_type (builtin_types[(int) RETURN], \
+ void_list_node);
+#define DEF_FUNCTION_TYPE_1(ENUM, RETURN, ARG1) \
+ builtin_types[(int) ENUM] \
+ = build_function_type (builtin_types[(int) RETURN], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG1], \
+ void_list_node));
+#define DEF_FUNCTION_TYPE_2(ENUM, RETURN, ARG1, ARG2) \
+ builtin_types[(int) ENUM] \
+ = build_function_type \
+ (builtin_types[(int) RETURN], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG1], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG2], \
+ void_list_node)));
+#define DEF_FUNCTION_TYPE_3(ENUM, RETURN, ARG1, ARG2, ARG3) \
+ builtin_types[(int) ENUM] \
+ = build_function_type \
+ (builtin_types[(int) RETURN], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG1], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG2], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG3], \
+ void_list_node))));
+#define DEF_FUNCTION_TYPE_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \
+ builtin_types[(int) ENUM] \
+ = build_function_type \
+ (builtin_types[(int) RETURN], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG1], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG2], \
+ tree_cons \
+ (NULL_TREE, \
+ builtin_types[(int) ARG3], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG4], \
+ void_list_node)))));
+#define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN) \
+ builtin_types[(int) ENUM] \
+ = build_function_type (builtin_types[(int) RETURN], NULL_TREE);
+#define DEF_FUNCTION_TYPE_VAR_1(ENUM, RETURN, ARG1) \
+ builtin_types[(int) ENUM] \
+ = build_function_type (builtin_types[(int) RETURN], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG1], \
NULL_TREE));
- builtin_function ("__builtin_constant_p", default_function_type,
- BUILT_IN_CONSTANT_P, BUILT_IN_NORMAL, NULL_PTR);
-
- builtin_function ("__builtin_return_address", ptr_ftype_unsigned,
- BUILT_IN_RETURN_ADDRESS, BUILT_IN_NORMAL, NULL_PTR);
-
- builtin_function ("__builtin_frame_address", ptr_ftype_unsigned,
- BUILT_IN_FRAME_ADDRESS, BUILT_IN_NORMAL, NULL_PTR);
-
- builtin_function ("__builtin_alloca", ptr_ftype_sizetype,
- BUILT_IN_ALLOCA, BUILT_IN_NORMAL, "alloca");
- builtin_function ("__builtin_ffs", int_ftype_int, BUILT_IN_FFS,
- BUILT_IN_NORMAL, NULL_PTR);
- /* Define alloca, ffs as builtins.
- Declare _exit just to mark it as volatile. */
- if (! flag_no_builtin && ! flag_no_nonansi_builtin)
- {
-#ifndef SMALL_STACK
- temp = builtin_function ("alloca", ptr_ftype_sizetype,
- BUILT_IN_ALLOCA, BUILT_IN_NORMAL, NULL_PTR);
- /* Suppress error if redefined as a non-function. */
- DECL_BUILT_IN_NONANSI (temp) = 1;
-#endif
- temp = builtin_function ("ffs", int_ftype_int, BUILT_IN_FFS,
- BUILT_IN_NORMAL, NULL_PTR);
- /* Suppress error if redefined as a non-function. */
- DECL_BUILT_IN_NONANSI (temp) = 1;
- temp = builtin_function ("_exit", void_ftype_int,
- 0, NOT_BUILT_IN, NULL_PTR);
- TREE_THIS_VOLATILE (temp) = 1;
- TREE_SIDE_EFFECTS (temp) = 1;
- /* 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. */
- temp = builtin_function ("bzero", void_ftype_any,
- BUILT_IN_BZERO, BUILT_IN_NORMAL, NULL_PTR);
- DECL_BUILT_IN_NONANSI (temp) = 1;
- temp = builtin_function ("bcmp", int_ftype_any,
- BUILT_IN_BCMP, BUILT_IN_NORMAL, NULL_PTR);
- DECL_BUILT_IN_NONANSI (temp) = 1;
- }
-
- builtin_function ("__builtin_abs", int_ftype_int, BUILT_IN_ABS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_fabsf", float_ftype_float, BUILT_IN_FABS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_fabs", double_ftype_double, BUILT_IN_FABS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_fabsl", ldouble_ftype_ldouble, BUILT_IN_FABS,
- 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_saveregs", ptr_ftype, BUILT_IN_SAVEREGS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_classify_type", default_function_type,
- BUILT_IN_CLASSIFY_TYPE, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_next_arg", ptr_ftype, BUILT_IN_NEXT_ARG,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_args_info", int_ftype_int, BUILT_IN_ARGS_INFO,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_setjmp",
- build_function_type (integer_type_node,
- tree_cons (NULL_TREE, ptr_type_node,
- endlink)),
- BUILT_IN_SETJMP, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_longjmp",
- build_function_type (void_type_node,
- tree_cons (NULL_TREE, ptr_type_node,
- tree_cons (NULL_TREE,
- integer_type_node,
- endlink))),
- BUILT_IN_LONGJMP, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_trap", void_ftype, BUILT_IN_TRAP,
- BUILT_IN_NORMAL, NULL_PTR);
-
- /* ISO C99 IEEE Unordered compares. */
- builtin_function ("__builtin_isgreater", default_function_type,
- BUILT_IN_ISGREATER, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_isgreaterequal", default_function_type,
- BUILT_IN_ISGREATEREQUAL, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_isless", default_function_type,
- BUILT_IN_ISLESS, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_islessequal", default_function_type,
- BUILT_IN_ISLESSEQUAL, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_islessgreater", default_function_type,
- BUILT_IN_ISLESSGREATER, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_isunordered", default_function_type,
- BUILT_IN_ISUNORDERED, BUILT_IN_NORMAL, NULL_PTR);
-
- /* Untyped call and return. */
- builtin_function ("__builtin_apply_args", ptr_ftype,
- BUILT_IN_APPLY_ARGS, BUILT_IN_NORMAL, NULL_PTR);
-
- temp = tree_cons (NULL_TREE,
- build_pointer_type (build_function_type (void_type_node,
- NULL_TREE)),
- tree_cons (NULL_TREE,
- ptr_type_node,
- tree_cons (NULL_TREE,
- sizetype,
- endlink)));
- builtin_function ("__builtin_apply",
- build_function_type (ptr_type_node, temp),
- BUILT_IN_APPLY, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_return", void_ftype_ptr,
- BUILT_IN_RETURN, BUILT_IN_NORMAL, NULL_PTR);
-
- /* Support for varargs.h and stdarg.h. */
- builtin_function ("__builtin_varargs_start",
- build_function_type (void_type_node,
- tree_cons (NULL_TREE,
- va_list_ref_type_node,
- endlink)),
- BUILT_IN_VARARGS_START, BUILT_IN_NORMAL, NULL_PTR);
-
- builtin_function ("__builtin_stdarg_start",
- build_function_type (void_type_node,
- tree_cons (NULL_TREE,
- va_list_ref_type_node,
- NULL_TREE)),
- BUILT_IN_STDARG_START, BUILT_IN_NORMAL, NULL_PTR);
-
- builtin_function ("__builtin_va_end",
- build_function_type (void_type_node,
- tree_cons (NULL_TREE,
- va_list_ref_type_node,
- endlink)),
- BUILT_IN_VA_END, BUILT_IN_NORMAL, NULL_PTR);
-
- builtin_function ("__builtin_va_copy",
- build_function_type (void_type_node,
- tree_cons (NULL_TREE,
- va_list_ref_type_node,
- tree_cons (NULL_TREE,
- va_list_arg_type_node,
- endlink))),
- BUILT_IN_VA_COPY, BUILT_IN_NORMAL, NULL_PTR);
-
- /* ??? Ought to be `T __builtin_expect(T, T)' for any type T. */
- builtin_function ("__builtin_expect",
- build_function_type (long_integer_type_node,
- tree_cons (NULL_TREE,
- long_integer_type_node,
- tree_cons (NULL_TREE,
- long_integer_type_node,
- endlink))),
- BUILT_IN_EXPECT, BUILT_IN_NORMAL, NULL_PTR);
-
- /* Currently under experimentation. */
- builtin_function ("__builtin_memcpy", memcpy_ftype, BUILT_IN_MEMCPY,
- BUILT_IN_NORMAL, "memcpy");
- builtin_function ("__builtin_memcmp", int_ftype_cptr_cptr_sizet,
- BUILT_IN_MEMCMP, BUILT_IN_NORMAL, "memcmp");
- builtin_function ("__builtin_memset", memset_ftype,
- BUILT_IN_MEMSET, BUILT_IN_NORMAL, "memset");
- builtin_function ("__builtin_bzero", bzero_ftype,
- BUILT_IN_BZERO, BUILT_IN_NORMAL, "bzero");
- builtin_function ("__builtin_bcmp", bcmp_ftype,
- BUILT_IN_BCMP, BUILT_IN_NORMAL, "bcmp");
- 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");
- builtin_function ("__builtin_strcmp", int_ftype_string_string,
- BUILT_IN_STRCMP, BUILT_IN_NORMAL, "strcmp");
- 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_strlen", strlen_ftype,
- BUILT_IN_STRLEN, BUILT_IN_NORMAL, "strlen");
- builtin_function ("__builtin_sqrtf", float_ftype_float,
- BUILT_IN_FSQRT, BUILT_IN_NORMAL, "sqrtf");
- builtin_function ("__builtin_fsqrt", double_ftype_double,
- BUILT_IN_FSQRT, BUILT_IN_NORMAL, "sqrt");
- builtin_function ("__builtin_sqrtl", ldouble_ftype_ldouble,
- BUILT_IN_FSQRT, BUILT_IN_NORMAL, "sqrtl");
- builtin_function ("__builtin_sinf", float_ftype_float,
- BUILT_IN_SIN, BUILT_IN_NORMAL, "sinf");
- builtin_function ("__builtin_sin", double_ftype_double,
- BUILT_IN_SIN, BUILT_IN_NORMAL, "sin");
- builtin_function ("__builtin_sinl", ldouble_ftype_ldouble,
- BUILT_IN_SIN, BUILT_IN_NORMAL, "sinl");
- builtin_function ("__builtin_cosf", float_ftype_float,
- BUILT_IN_COS, BUILT_IN_NORMAL, "cosf");
- builtin_function ("__builtin_cos", double_ftype_double,
- 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");
- built_in_decls[BUILT_IN_FPUTS] =
- builtin_function ("__builtin_fputs", int_ftype_any,
- BUILT_IN_FPUTS, BUILT_IN_NORMAL, "fputs");
-
- if (! flag_no_builtin)
- {
- builtin_function ("abs", int_ftype_int, BUILT_IN_ABS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("fabsf", float_ftype_float, BUILT_IN_FABS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("fabs", double_ftype_double, BUILT_IN_FABS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("fabsl", ldouble_ftype_ldouble, BUILT_IN_FABS,
- 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 ("memcpy", memcpy_ftype, BUILT_IN_MEMCPY,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("memcmp", int_ftype_cptr_cptr_sizet, BUILT_IN_MEMCMP,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("memset", memset_ftype, BUILT_IN_MEMSET,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("strcmp", int_ftype_string_string, BUILT_IN_STRCMP,
- 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 ("strlen", strlen_ftype, BUILT_IN_STRLEN,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("sqrtf", float_ftype_float, BUILT_IN_FSQRT,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("sqrt", double_ftype_double, BUILT_IN_FSQRT,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("sqrtl", ldouble_ftype_ldouble, BUILT_IN_FSQRT,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("sinf", float_ftype_float, BUILT_IN_SIN,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("sin", double_ftype_double, BUILT_IN_SIN,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("sinl", ldouble_ftype_ldouble, BUILT_IN_SIN,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("cosf", float_ftype_float, BUILT_IN_COS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("cos", double_ftype_double, BUILT_IN_COS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("cosl", ldouble_ftype_ldouble, BUILT_IN_COS,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("printf", printf_ftype, BUILT_IN_PRINTF,
- BUILT_IN_FRONTEND, NULL_PTR);
- /* We declare these without argument so that the initial
- declaration for these identifiers is a builtin. That allows
- us to redeclare them later with argument without worrying
- about the explicit declarations in stdio.h being taken as the
- initial declaration. */
- builtin_function ("fputc", int_ftype_any, BUILT_IN_FPUTC,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("fputs", int_ftype_any, BUILT_IN_FPUTS,
- BUILT_IN_NORMAL, NULL_PTR);
-
- /* Declare these functions volatile
- to avoid spurious "control drops through" warnings. */
- temp = builtin_function ("abort",
- ((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;
-
- temp = builtin_function ("exit",
- ((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
- /* Support for these has not been written in either expand_builtin
- or build_function_call. */
- builtin_function ("__builtin_div", default_ftype, BUILT_IN_DIV,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_ldiv", default_ftype, BUILT_IN_LDIV,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_ffloor", double_ftype_double, BUILT_IN_FFLOOR,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_fceil", double_ftype_double, BUILT_IN_FCEIL,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_fmod", double_ftype_double_double,
- BUILT_IN_FMOD, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_frem", double_ftype_double_double,
- BUILT_IN_FREM, BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_getexp", double_ftype_double, BUILT_IN_GETEXP,
- BUILT_IN_NORMAL, NULL_PTR);
- builtin_function ("__builtin_getman", double_ftype_double, BUILT_IN_GETMAN,
- BUILT_IN_NORMAL, NULL_PTR);
-#endif
+#define DEF_FUNCTION_TYPE_VAR_2(ENUM, RETURN, ARG1, ARG2) \
+ builtin_types[(int) ENUM] \
+ = build_function_type \
+ (builtin_types[(int) RETURN], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG1], \
+ tree_cons (NULL_TREE, \
+ builtin_types[(int) ARG2], \
+ NULL_TREE)));
+#define DEF_POINTER_TYPE(ENUM, TYPE) \
+ builtin_types[(int) ENUM] \
+ = build_pointer_type (builtin_types[(int) TYPE]);
+#include "builtin-types.def"
+#undef DEF_PRIMITIVE_TYPE
+#undef DEF_FUNCTION_TYPE_1
+#undef DEF_FUNCTION_TYPE_2
+#undef DEF_FUNCTION_TYPE_3
+#undef DEF_FUNCTION_TYPE_4
+#undef DEF_FUNCTION_TYPE_VAR_0
+#undef DEF_FUNCTION_TYPE_VAR_1
+#undef DEF_POINTER_TYPE
+
+#define DEF_BUILTIN(ENUM, NAME, CLASS, \
+ TYPE, LIBTYPE, BOTH_P, FALLBACK_P, NONANSI_P) \
+ if (NAME) \
+ { \
+ tree decl; \
+ \
+ if (strncmp (NAME, "__builtin_", strlen ("__builtin_")) != 0) \
+ abort (); \
+ \
+ if (!BOTH_P) \
+ decl = builtin_function (NAME, builtin_types[TYPE], ENUM, \
+ CLASS, \
+ (FALLBACK_P \
+ ? (NAME + strlen ("__builtin_")) \
+ : NULL)); \
+ else \
+ decl = builtin_function_2 (NAME, \
+ NAME + strlen ("__builtin_"), \
+ builtin_types[TYPE], \
+ builtin_types[LIBTYPE], \
+ ENUM, \
+ CLASS, \
+ FALLBACK_P, \
+ NONANSI_P, \
+ /*noreturn_p=*/0); \
+ \
+ built_in_decls[(int) ENUM] = decl; \
+ }
+#include "builtins.def"
+#undef DEF_BUILTIN
+
+ /* Declare _exit and _Exit just to mark them as non-returning. */
+ builtin_function_2 (NULL, "_exit", NULL_TREE,
+ builtin_types[BT_FN_VOID_INT],
+ 0, NOT_BUILT_IN, 0, 1, 1);
+ builtin_function_2 (NULL, "_Exit", NULL_TREE,
+ builtin_types[BT_FN_VOID_INT],
+ 0, NOT_BUILT_IN, 0, !flag_isoc99, 1);
+
+ /* Declare these functions non-returning
+ to avoid spurious "control drops through" warnings. */
+ builtin_function_2 (NULL, "abort",
+ NULL_TREE, ((c_language == clk_cplusplus)
+ ? builtin_types[BT_FN_VOID]
+ : builtin_types[BT_FN_VOID_VAR]),
+ 0, NOT_BUILT_IN, 0, 0, 1);
+
+ builtin_function_2 (NULL, "exit",
+ NULL_TREE, ((c_language == clk_cplusplus)
+ ? builtin_types[BT_FN_VOID_INT]
+ : builtin_types[BT_FN_VOID_VAR]),
+ 0, NOT_BUILT_IN, 0, 0, 1);
main_identifier_node = get_identifier ("main");
{
return build1 (VA_ARG_EXPR, type, expr);
}
+
+
+/* Linked list of disabled built-in functions. */
+
+typedef struct disabled_builtin
+{
+ const char *name;
+ struct disabled_builtin *next;
+} disabled_builtin;
+static disabled_builtin *disabled_builtins = NULL;
+
+static bool builtin_function_disabled_p PARAMS ((const char *));
+
+/* Disable a built-in function specified by -fno-builtin-NAME. If NAME
+ begins with "__builtin_", give an error. */
+
+void
+disable_builtin_function (name)
+ const char *name;
+{
+ if (strncmp (name, "__builtin_", strlen ("__builtin_")) == 0)
+ error ("cannot disable built-in function `%s'", name);
+ else
+ {
+ disabled_builtin *new = xmalloc (sizeof (disabled_builtin));
+ new->name = name;
+ new->next = disabled_builtins;
+ disabled_builtins = new;
+ }
+}
+
+
+/* Return true if the built-in function NAME has been disabled, false
+ otherwise. */
+
+static bool
+builtin_function_disabled_p (name)
+ const char *name;
+{
+ disabled_builtin *p;
+ for (p = disabled_builtins; p != NULL; p = p->next)
+ {
+ if (strcmp (name, p->name) == 0)
+ return true;
+ }
+ return false;
+}
+
+
+/* Possibly define a builtin function with one or two names. BUILTIN_NAME
+ is an __builtin_-prefixed name; NAME is the ordinary name; one or both
+ of these may be NULL (though both being NULL is useless).
+ BUILTIN_TYPE is the type of the __builtin_-prefixed function;
+ TYPE is the type of the function with the ordinary name. These
+ may differ if the ordinary name is declared with a looser type to avoid
+ conflicts with headers. FUNCTION_CODE and CLASS are as for
+ builtin_function. If LIBRARY_NAME_P is nonzero, NAME is passed as
+ the LIBRARY_NAME parameter to builtin_function when declaring BUILTIN_NAME.
+ If NONANSI_P is nonzero, the name NAME is treated as a non-ANSI name; if
+ NORETURN_P is nonzero, the function is marked as non-returning.
+ Returns the declaration of BUILTIN_NAME, if any, otherwise
+ the declaration of NAME. Does not declare NAME if flag_no_builtin,
+ or if NONANSI_P and flag_no_nonansi_builtin. */
+
+static tree
+builtin_function_2 (builtin_name, name, builtin_type, type, function_code,
+ class, library_name_p, nonansi_p, noreturn_p)
+ const char *builtin_name;
+ const char *name;
+ tree builtin_type;
+ tree type;
+ int function_code;
+ enum built_in_class class;
+ int library_name_p;
+ int nonansi_p;
+ int noreturn_p;
+{
+ tree bdecl = NULL_TREE;
+ tree decl = NULL_TREE;
+ if (builtin_name != 0)
+ {
+ bdecl = builtin_function (builtin_name, builtin_type, function_code,
+ class, library_name_p ? name : NULL);
+ if (noreturn_p)
+ {
+ TREE_THIS_VOLATILE (bdecl) = 1;
+ TREE_SIDE_EFFECTS (bdecl) = 1;
+ }
+ }
+ if (name != 0 && !flag_no_builtin && !builtin_function_disabled_p (name)
+ && !(nonansi_p && flag_no_nonansi_builtin))
+ {
+ decl = builtin_function (name, type, function_code, class, NULL);
+ if (nonansi_p)
+ DECL_BUILT_IN_NONANSI (decl) = 1;
+ if (noreturn_p)
+ {
+ TREE_THIS_VOLATILE (decl) = 1;
+ TREE_SIDE_EFFECTS (decl) = 1;
+ }
+ }
+ return (bdecl != 0 ? bdecl : decl);
+}
\f
+/* Nonzero if the type T promotes to int. This is (nearly) the
+ integral promotions defined in ISO C99 6.3.1.1/2. */
+
+bool
+c_promoting_integer_type_p (t)
+ tree t;
+{
+ switch (TREE_CODE (t))
+ {
+ case INTEGER_TYPE:
+ return (TYPE_MAIN_VARIANT (t) == char_type_node
+ || TYPE_MAIN_VARIANT (t) == signed_char_type_node
+ || TYPE_MAIN_VARIANT (t) == unsigned_char_type_node
+ || TYPE_MAIN_VARIANT (t) == short_integer_type_node
+ || TYPE_MAIN_VARIANT (t) == short_unsigned_type_node
+ || TYPE_PRECISION (t) < TYPE_PRECISION (integer_type_node));
+
+ case ENUMERAL_TYPE:
+ /* ??? Technically all enumerations not larger than an int
+ promote to an int. But this is used along code paths
+ that only want to notice a size change. */
+ return TYPE_PRECISION (t) < TYPE_PRECISION (integer_type_node);
+
+ case BOOLEAN_TYPE:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
/* Given a type, apply default promotions wrt unnamed function arguments
and return the new type. Return NULL_TREE if no change. */
/* ??? There is a function of the same name in the C++ front end that
if (TYPE_MAIN_VARIANT (type) == float_type_node)
return double_type_node;
- if (C_PROMOTING_INTEGER_TYPE_P (type))
+ if (c_promoting_integer_type_p (type))
{
/* Traditionally, unsignedness is preserved in default promotions.
Also preserve unsignedness if not really getting any wider. */
self_promoting_args_p (parms)
tree parms;
{
- register tree t;
+ tree t;
for (t = parms; t; t = TREE_CHAIN (t))
{
- register tree type = TREE_VALUE (t);
+ tree type = TREE_VALUE (t);
if (TREE_CHAIN (t) == 0 && type != void_type_node)
return 0;
if (TYPE_MAIN_VARIANT (type) == float_type_node)
return 0;
- if (C_PROMOTING_INTEGER_TYPE_P (type))
+ if (c_promoting_integer_type_p (type))
return 0;
}
return 1;
case BUILT_IN_ABS:
case BUILT_IN_LABS:
case BUILT_IN_LLABS:
+ case BUILT_IN_IMAXABS:
case BUILT_IN_FABS:
+ case BUILT_IN_FABSL:
+ case BUILT_IN_FABSF:
if (coerced_params == 0)
return integer_zero_node;
return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0);
+ case BUILT_IN_CONJ:
+ case BUILT_IN_CONJF:
+ case BUILT_IN_CONJL:
+ if (coerced_params == 0)
+ return integer_zero_node;
+ return build_unary_op (CONJ_EXPR, TREE_VALUE (coerced_params), 0);
+
+ case BUILT_IN_CREAL:
+ case BUILT_IN_CREALF:
+ case BUILT_IN_CREALL:
+ if (coerced_params == 0)
+ return integer_zero_node;
+ return build_unary_op (REALPART_EXPR, TREE_VALUE (coerced_params), 0);
+
+ case BUILT_IN_CIMAG:
+ case BUILT_IN_CIMAGF:
+ case BUILT_IN_CIMAGL:
+ if (coerced_params == 0)
+ return integer_zero_node;
+ return build_unary_op (IMAGPART_EXPR, TREE_VALUE (coerced_params), 0);
+
case BUILT_IN_ISGREATER:
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT)
code = UNLE_EXPR;
}
}
-/* Walk the statemen tree, rooted at *tp. Apply FUNC to all the
+/* Walk the statement tree, rooted at *tp. Apply FUNC to all the
sub-trees of *TP in a pre-order traversal. FUNC is called with the
DATA and the address of each sub-tree. If FUNC returns a non-NULL
value, the traversal is aborted, and the value returned by FUNC is
if (result)
return result;
- /* Even if we didn't, FUNC may have decided that there was nothing
- interesting below this point in the tree. */
- if (!walk_subtrees)
- return NULL_TREE;
-
/* FUNC may have modified the tree, recheck that we're looking at a
statement node. */
code = TREE_CODE (*tp);
if (!statement_code_p (code))
return NULL_TREE;
- /* Walk over all the sub-trees of this operand. Statement nodes never
- contain RTL, and we needn't worry about TARGET_EXPRs. */
- len = TREE_CODE_LENGTH (code);
+ /* Visit the subtrees unless FUNC decided that there was nothing
+ interesting below this point in the tree. */
+ if (walk_subtrees)
+ {
+ /* Walk over all the sub-trees of this operand. Statement nodes
+ never contain RTL, and we needn't worry about TARGET_EXPRs. */
+ len = TREE_CODE_LENGTH (code);
- /* Go through the subtrees. We need to do this in forward order so
- that the scope of a FOR_EXPR is handled properly. */
- for (i = 0; i < len; ++i)
- WALK_SUBTREE (TREE_OPERAND (*tp, i));
+ /* Go through the subtrees. We need to do this in forward order so
+ that the scope of a FOR_EXPR is handled properly. */
+ for (i = 0; i < len; ++i)
+ WALK_SUBTREE (TREE_OPERAND (*tp, i));
+ }
/* Finally visit the chain. This can be tail-recursion optimized if
we write it this way. */
return case_label;
}
+/* Finish an expression taking the address of LABEL. Returns an
+ expression for the address. */
+
+tree
+finish_label_address_expr (label)
+ tree label;
+{
+ tree result;
+
+ if (pedantic)
+ {
+ if (c_language == clk_cplusplus)
+ pedwarn ("ISO C++ forbids taking the address of a label");
+ else
+ pedwarn ("ISO C forbids taking the address of a label");
+ }
+
+ label = lookup_label (label);
+ if (label == NULL_TREE)
+ result = null_pointer_node;
+ else
+ {
+ TREE_USED (label) = 1;
+ result = build1 (ADDR_EXPR, ptr_type_node, label);
+ TREE_CONSTANT (result) = 1;
+ /* The current function in not necessarily uninlinable.
+ Computed gotos are incompatible with inlining, but the value
+ here could be used only in a diagnostic, for example. */
+ }
+
+ return result;
+}
+
/* 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
c_mark_lang_decl (c)
- struct c_lang_decl *c;
+ struct c_lang_decl *c ATTRIBUTE_UNUSED;
{
- ggc_mark_tree (c->saved_tree);
}
/* Mark F for GC. */
}
break;
+ case COMPOUND_LITERAL_EXPR:
+ {
+ /* Initialize the anonymous variable declared in the compound
+ literal, then return the variable. */
+ tree decl = COMPOUND_LITERAL_EXPR_DECL (exp);
+ emit_local_var (decl);
+ return expand_expr (decl, target, tmode, modifier);
+ }
+
default:
abort ();
}
return 1;
}
-/* Tree code classes. */
+/* 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;
+}
+
+/* Hook used by staticp to handle language-specific tree codes. */
+
+int
+c_staticp (exp)
+ tree exp;
+{
+ if (TREE_CODE (exp) == COMPOUND_LITERAL_EXPR
+ && TREE_STATIC (COMPOUND_LITERAL_EXPR_DECL (exp)))
+ return 1;
+ return 0;
+}
+
+/* Tree code classes. */
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,
-static char c_tree_code_type[] = {
+static const char c_tree_code_type[] = {
'x',
#include "c-common.def"
};
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH,
-static int c_tree_code_length[] = {
+static const int c_tree_code_length[] = {
0,
#include "c-common.def"
};
Used for printing out the tree and error messages. */
#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,
-static const char *c_tree_code_name[] = {
+static const char *const c_tree_code_name[] = {
"@@dummy",
#include "c-common.def"
};
#undef DEFTREECODE
/* Adds the tree codes specific to the C front end to the list of all
- tree codes. */
+ tree codes. */
void
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) \
return target;
break;
+ case BUILT_IN_FPRINTF:
+ target = c_expand_builtin_fprintf (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)));
/* Check an arglist to *printf for problems. The arglist should start
at the format specifier, with the remaining arguments immediately
- following it. */
+ following it. */
static int
is_valid_printf_arglist (arglist)
tree arglist;
{
- /* Save this value so we can restore it later. */
+ /* Save this value so we can restore it later. */
const int SAVE_pedantic = pedantic;
int diagnostic_occurred = 0;
+ tree attrs;
/* 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'. */
+ /* Check to make sure there are no format specifier errors. */
+ attrs = tree_cons (get_identifier ("format"),
+ tree_cons (NULL_TREE,
+ get_identifier ("printf"),
+ tree_cons (NULL_TREE,
+ integer_one_node,
+ tree_cons (NULL_TREE,
+ build_int_2 (2, 0),
+ NULL_TREE))),
+ NULL_TREE);
+ check_function_format (&diagnostic_occurred, attrs, 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 false, otherwise we return true. */
return ! diagnostic_occurred;
}
/* If the arguments passed to printf are suitable for optimizations,
- we attempt to transform the call. */
+ we attempt to transform the call. */
static rtx
c_expand_builtin_printf (arglist, target, tmode, modifier, ignore)
tree arglist;
tree fn, format_arg, stripped_string;
/* If the return value is used, or the replacement _DECL isn't
- initialized, don't do the transformation. */
+ initialized, don't do the transformation. */
if (!ignore || !fn_putchar || !fn_puts)
return 0;
- /* Verify the required arguments in the original call. */
+ /* 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. */
+ /* Check the specifier vs. the parameters. */
if (!is_valid_printf_arglist (arglist))
return 0;
/* OK! We can attempt optimization. */
- /* If the format specifier was "%s\n", call __builtin_puts(arg2). */
+ /* 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). */
+ /* If the format specifier was "%c", call __builtin_putchar (arg2). */
else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
{
arglist = TREE_CHAIN (arglist);
}
else
{
- /* We can't handle anything else with % args or %% ... yet. */
+ /* We can't handle anything else with % args or %% ... yet. */
if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
return 0;
{
/* Given printf("c"), (where c is any one character,)
convert "c"[0] to an int and pass that to the replacement
- function. */
+ function. */
arglist = build_int_2 (TREE_STRING_POINTER (stripped_string)[0], 0);
arglist = build_tree_list (NULL_TREE, arglist);
(ignore ? const0_rtx : target),
tmode, modifier);
}
+
+/* If the arguments passed to fprintf are suitable for optimizations,
+ we attempt to transform the call. */
+static rtx
+c_expand_builtin_fprintf (arglist, target, tmode, modifier, ignore)
+ tree arglist;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+ int ignore;
+{
+ tree fn_fputc = built_in_decls[BUILT_IN_FPUTC],
+ fn_fputs = built_in_decls[BUILT_IN_FPUTS];
+ 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_fputc || !fn_fputs)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ if (arglist == 0
+ || (TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+ || (TREE_CHAIN (arglist) == 0)
+ || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) !=
+ POINTER_TYPE))
+ return 0;
+
+ /* Check the specifier vs. the parameters. */
+ if (!is_valid_printf_arglist (TREE_CHAIN (arglist)))
+ return 0;
+
+ format_arg = TREE_VALUE (TREE_CHAIN (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", call __builtin_fputs(arg3, arg1). */
+ if (strcmp (TREE_STRING_POINTER (stripped_string), "%s") == 0)
+ {
+ tree newarglist = build_tree_list (NULL_TREE, TREE_VALUE (arglist));
+ arglist = tree_cons (NULL_TREE,
+ TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))),
+ newarglist);
+ fn = fn_fputs;
+ }
+ /* If the format specifier was "%c", call __builtin_fputc (arg3, arg1). */
+ else if (strcmp (TREE_STRING_POINTER (stripped_string), "%c") == 0)
+ {
+ tree newarglist = build_tree_list (NULL_TREE, TREE_VALUE (arglist));
+ arglist = tree_cons (NULL_TREE,
+ TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))),
+ newarglist);
+ fn = fn_fputc;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (TREE_STRING_POINTER (stripped_string), '%'))
+ return 0;
+
+ /* When "string" doesn't contain %, replace all cases of
+ fprintf(stream,string) with fputs(string,stream). The fputs
+ builtin will take take of special cases like length==1. */
+ arglist = tree_cons (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)),
+ build_tree_list (NULL_TREE, TREE_VALUE (arglist)));
+ fn = fn_fputs;
+ }
+
+ return expand_expr (build_function_call (fn, arglist),
+ (ignore ? const0_rtx : target),
+ tmode, modifier);
+}
+\f
+
+/* Given a boolean expression ARG, return a tree representing an increment
+ or decrement (as indicated by CODE) of ARG. The front end must check for
+ invalid cases (e.g., decrement in C++). */
+tree
+boolean_increment (code, arg)
+ enum tree_code code;
+ tree arg;
+{
+ tree val;
+ tree true_res = (c_language == clk_cplusplus
+ ? boolean_true_node
+ : c_bool_true_node);
+ arg = stabilize_reference (arg);
+ switch (code)
+ {
+ case PREINCREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res);
+ break;
+ case POSTINCREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res);
+ arg = save_expr (arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val);
+ break;
+ case PREDECREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg));
+ break;
+ case POSTDECREMENT_EXPR:
+ val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg));
+ arg = save_expr (arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg);
+ val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val);
+ break;
+ default:
+ abort ();
+ }
+ TREE_SIDE_EFFECTS (val) = 1;
+ return val;
+}
+\f
+/* Handle C and C++ default attributes. */
+
+enum built_in_attribute
+{
+#define DEF_ATTR_NULL_TREE(ENUM) ENUM,
+#define DEF_ATTR_INT(ENUM, VALUE) ENUM,
+#define DEF_ATTR_IDENT(ENUM, STRING) ENUM,
+#define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) ENUM,
+#define DEF_FN_ATTR(NAME, ATTRS, PREDICATE) /* No entry needed in enum. */
+#include "builtin-attrs.def"
+#undef DEF_ATTR_NULL_TREE
+#undef DEF_ATTR_INT
+#undef DEF_ATTR_IDENT
+#undef DEF_ATTR_TREE_LIST
+#undef DEF_FN_ATTR
+ ATTR_LAST
+};
+
+static tree built_in_attributes[(int) ATTR_LAST];
+
+static bool c_attrs_initialized = false;
+
+static void c_init_attributes PARAMS ((void));
+
+/* Common initialization before parsing options. */
+void
+c_common_init_options (lang)
+ enum c_language_kind lang;
+{
+ c_language = lang;
+ parse_in = cpp_create_reader (lang == clk_c ? CLK_GNUC89:
+ lang == clk_cplusplus ? CLK_GNUCXX: CLK_OBJC);
+
+ /* Mark as "unspecified" (see c_common_post_options). */
+ flag_bounds_check = -1;
+}
+
+/* Post-switch processing. */
+void
+c_common_post_options ()
+{
+ cpp_post_options (parse_in);
+
+ /* Use tree inlining if possible. Function instrumentation is only
+ done in the RTL level, so we disable tree inlining. */
+ if (! flag_instrument_function_entry_exit)
+ {
+ if (!flag_no_inline)
+ {
+ flag_inline_trees = 1;
+ flag_no_inline = 1;
+ }
+ if (flag_inline_functions)
+ {
+ flag_inline_trees = 2;
+ flag_inline_functions = 0;
+ }
+ }
+
+ /* If still "unspecified", make it match -fbounded-pointers. */
+ if (flag_bounds_check == -1)
+ flag_bounds_check = flag_bounded_pointers;
+
+ /* Special format checking options don't work without -Wformat; warn if
+ they are used. */
+ if (warn_format_y2k && !warn_format)
+ warning ("-Wformat-y2k ignored without -Wformat");
+ if (warn_format_extra_args && !warn_format)
+ warning ("-Wformat-extra-args ignored without -Wformat");
+ if (warn_format_nonliteral && !warn_format)
+ warning ("-Wformat-nonliteral ignored without -Wformat");
+ if (warn_format_security && !warn_format)
+ warning ("-Wformat-security ignored without -Wformat");
+ if (warn_missing_format_attribute && !warn_format)
+ warning ("-Wmissing-format-attribute ignored without -Wformat");
+}
+
+/* Front end initialization common to C, ObjC and C++. */
+const char *
+c_common_init (filename)
+ const char *filename;
+{
+ /* Do this before initializing pragmas, as then cpplib's hash table
+ has been set up. */
+ filename = init_c_lex (filename);
+
+ init_pragma ();
+
+ if (!c_attrs_initialized)
+ c_init_attributes ();
+
+ return filename;
+}
+
+/* Common finish hook for the C, ObjC and C++ front ends. */
+void
+c_common_finish ()
+{
+ cpp_finish (parse_in);
+
+ /* For performance, avoid tearing down cpplib's internal structures.
+ Call cpp_errors () instead of cpp_destroy (). */
+ errorcount += cpp_errors (parse_in);
+}
+
+static void
+c_init_attributes ()
+{
+ /* Fill in the built_in_attributes array. */
+#define DEF_ATTR_NULL_TREE(ENUM) \
+ built_in_attributes[(int) ENUM] = NULL_TREE;
+#define DEF_ATTR_INT(ENUM, VALUE) \
+ built_in_attributes[(int) ENUM] = build_int_2 (VALUE, VALUE < 0 ? -1 : 0);
+#define DEF_ATTR_IDENT(ENUM, STRING) \
+ built_in_attributes[(int) ENUM] = get_identifier (STRING);
+#define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) \
+ built_in_attributes[(int) ENUM] \
+ = tree_cons (built_in_attributes[(int) PURPOSE], \
+ built_in_attributes[(int) VALUE], \
+ built_in_attributes[(int) CHAIN]);
+#define DEF_FN_ATTR(NAME, ATTRS, PREDICATE) /* No initialization needed. */
+#include "builtin-attrs.def"
+#undef DEF_ATTR_NULL_TREE
+#undef DEF_ATTR_INT
+#undef DEF_ATTR_IDENT
+#undef DEF_ATTR_TREE_LIST
+#undef DEF_FN_ATTR
+ ggc_add_tree_root (built_in_attributes, (int) ATTR_LAST);
+ c_attrs_initialized = true;
+}
+
+/* Depending on the name of DECL, apply default attributes to it. */
+
+void
+c_common_insert_default_attributes (decl)
+ tree decl;
+{
+ tree name = DECL_NAME (decl);
+
+ if (!c_attrs_initialized)
+ c_init_attributes ();
+
+#define DEF_ATTR_NULL_TREE(ENUM) /* Nothing needed after initialization. */
+#define DEF_ATTR_INT(ENUM, VALUE)
+#define DEF_ATTR_IDENT(ENUM, STRING)
+#define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN)
+#define DEF_FN_ATTR(NAME, ATTRS, PREDICATE) \
+ if ((PREDICATE) && name == built_in_attributes[(int) NAME]) \
+ decl_attributes (&decl, built_in_attributes[(int) ATTRS], \
+ ATTR_FLAG_BUILT_IN);
+#include "builtin-attrs.def"
+#undef DEF_ATTR_NULL_TREE
+#undef DEF_ATTR_INT
+#undef DEF_ATTR_IDENT
+#undef DEF_ATTR_TREE_LIST
+#undef DEF_FN_ATTR
+}
+
+/* Output a -Wshadow warning MSGID about NAME, an IDENTIFIER_NODE, and
+ additionally give the location of the previous declaration DECL. */
+void
+shadow_warning (msgid, name, decl)
+ const char *msgid;
+ tree name, decl;
+{
+ warning ("declaration of `%s' shadows %s", IDENTIFIER_POINTER (name), msgid);
+ warning_with_file_and_line (DECL_SOURCE_FILE (decl),
+ DECL_SOURCE_LINE (decl),
+ "shadowed declaration is here");
+}
+