/* Subroutines shared by all languages that are variants of C.
- Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
This file is part of GNU CC.
Boston, MA 02111-1307, USA. */
#include "config.h"
+#include "system.h"
#include "tree.h"
#include "c-lex.h"
#include "c-tree.h"
#include "flags.h"
#include "obstack.h"
-#include <stdio.h>
-#include <ctype.h>
+#include "toplev.h"
+#include "output.h"
+#include "c-pragma.h"
+
+#if USE_CPPLIB
+#include "cpplib.h"
+cpp_reader parse_in;
+cpp_options parse_options;
+static enum cpp_token cpp_token;
+#endif
#ifndef WCHAR_TYPE_SIZE
#ifdef INT_TYPE_SIZE
int skip_evaluation;
enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
+ 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_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS,
+ A_INIT_PRIORITY};
+
+enum format_type { printf_format_type, scanf_format_type,
+ strftime_format_type };
static void declare_hidden_char_array PROTO((char *, char *));
static void add_attribute PROTO((enum attrs, char *,
int, int, int));
static void init_attributes PROTO((void));
+static void record_function_format PROTO((tree, tree, enum format_type,
+ int, int));
static void record_international_format PROTO((tree, tree, int));
+/* 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
+ and file of the if. If a potentially ambiguous else is seen, that
+ fact is recorded; the warning is issued when we can be sure that
+ the enclosing if statement does not have an else branch. */
+typedef struct
+{
+ int compstmt_count;
+ int line;
+ char *file;
+ int needs_warning;
+} if_elt;
+
+static if_elt *if_stack;
+
+/* Amount of space in the if statement stack. */
+static int if_stack_space = 0;
+
+/* Stack pointer. */
+static int if_stack_pointer = 0;
+
+/* Generate RTL for the start of an if-then, and record the start of it
+ for ambiguous else detection. */
+
+/* A list of objects which have constructors or destructors which
+ reside in the global scope, and have an init_priority attribute
+ associated with them. The decl is stored in the TREE_VALUE slot
+ and the priority number is stored in the TREE_PURPOSE slot. */
+tree static_aggregates_initp;
+
+void
+c_expand_start_cond (cond, exitflag, compstmt_count)
+ tree cond;
+ int exitflag;
+ int compstmt_count;
+{
+ /* Make sure there is enough space on the stack. */
+ if (if_stack_space == 0)
+ {
+ if_stack_space = 10;
+ if_stack = (if_elt *)xmalloc (10 * sizeof (if_elt));
+ }
+ else if (if_stack_space == if_stack_pointer)
+ {
+ if_stack_space += 10;
+ if_stack = (if_elt *)xrealloc (if_stack, if_stack_space * sizeof (if_elt));
+ }
+
+ /* Record this if statement. */
+ if_stack[if_stack_pointer].compstmt_count = compstmt_count;
+ if_stack[if_stack_pointer].file = input_filename;
+ if_stack[if_stack_pointer].line = lineno;
+ if_stack[if_stack_pointer].needs_warning = 0;
+ if_stack_pointer++;
+
+ expand_start_cond (cond, exitflag);
+}
+
+/* Generate RTL for the end of an if-then. Optionally warn if a nested
+ if statement had an ambiguous else clause. */
+
+void
+c_expand_end_cond ()
+{
+ if_stack_pointer--;
+ if (if_stack[if_stack_pointer].needs_warning)
+ warning_with_file_and_line (if_stack[if_stack_pointer].file,
+ if_stack[if_stack_pointer].line,
+ "suggest explicit braces to avoid ambiguous `else'");
+ expand_end_cond ();
+}
+
+/* Generate RTL between the then-clause and the else-clause
+ of an if-then-else. */
+
+void
+c_expand_start_else ()
+{
+ /* An ambiguous else warning must be generated for the enclosing if
+ statement, unless we see an else branch for that one, too. */
+ if (warn_parentheses
+ && if_stack_pointer > 1
+ && (if_stack[if_stack_pointer - 1].compstmt_count
+ == if_stack[if_stack_pointer - 2].compstmt_count))
+ if_stack[if_stack_pointer - 2].needs_warning = 1;
+
+ /* Even if a nested if statement had an else branch, it can't be
+ ambiguous if this one also has an else. So don't warn in that
+ case. Also don't warn for any if statements nested in this else. */
+ if_stack[if_stack_pointer - 1].needs_warning = 0;
+ if_stack[if_stack_pointer - 1].compstmt_count--;
+
+ expand_start_else ();
+}
+
/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
void
or if we want to give warnings for large objects, make a bigger one. */
vlen = strlen (value) + 1;
type = char_array_type_node;
- if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (type))) < vlen
+ if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) < vlen
|| warn_larger_than)
type = build_array_type (char_type_node,
build_index_type (build_int_2 (vlen, 0)));
? wchar_bytes : 1));
if ((TREE_TYPE (t) == wchar_array_type_node) == wide_flag)
{
- bcopy (TREE_STRING_POINTER (t), q, len);
+ memcpy (q, TREE_STRING_POINTER (t), len);
q += len;
}
else
value = make_node (STRING_CST);
TREE_STRING_POINTER (value) = p;
TREE_STRING_LENGTH (value) = length;
- TREE_CONSTANT (value) = 1;
}
else
{
wide_flag = 1;
}
- /* Compute the number of elements, for the array type. */
+ /* Compute the number of elements, for the array type. */
nchars = wide_flag ? length / wchar_bytes : length;
/* Create the array type for the string constant.
-Wwrite-strings says make the string constant an array of const char
- so that copying it to a non-const pointer will get a warning. */
- if (warn_write_strings
+ so that copying it to a non-const pointer will get a warning.
+ For C++, this is the standard behavior. */
+ if (flag_const_strings
&& (! flag_traditional && ! flag_writable_strings))
{
tree elements
TREE_TYPE (value)
= build_array_type (wide_flag ? wchar_type_node : char_type_node,
build_index_type (build_int_2 (nchars - 1, 0)));
- TREE_CONSTANT (value) = 1;
+
+ TREE_READONLY (value) = TREE_CONSTANT (value) = ! flag_writable_strings;
TREE_STATIC (value) = 1;
return value;
}
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, 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_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_INIT_PRIORITY, "init_priority", 0, 1, 0);
+ add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1);
}
\f
/* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES
decl_attributes (node, attributes, prefix_attributes)
tree node, attributes, prefix_attributes;
{
- tree decl = 0, type;
- int is_type;
+ tree decl = 0, type = 0;
+ int is_type = 0;
tree a;
if (attrtab_idx == 0)
else if (TREE_CODE_CLASS (TREE_CODE (node)) == 't')
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))
tree args = TREE_VALUE (a);
int i;
enum attrs id;
-
+
for (i = 0; i < attrtab_idx; i++)
if (attrtab[i].name == name)
break;
TREE_THIS_VOLATILE (decl) = 1;
else if (TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
- TREE_TYPE (decl) = type
+ TREE_TYPE (decl) = type
= build_pointer_type
(build_type_variant (TREE_TYPE (type),
TREE_READONLY (TREE_TYPE (type)), 1));
break;
case A_UNUSED:
- if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL
- || TREE_CODE (decl) == FUNCTION_DECL)
+ if (is_type)
+ TREE_USED (type) = 1;
+ else if (TREE_CODE (decl) == PARM_DECL
+ || TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == FUNCTION_DECL)
TREE_USED (decl) = 1;
else
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
|| TREE_CODE (decl) == VAR_DECL)
&& TREE_CODE (TREE_VALUE (args)) == STRING_CST)
{
- if (TREE_CODE (decl) == VAR_DECL
+ if (TREE_CODE (decl) == VAR_DECL
&& current_function_decl != NULL_TREE
&& ! TREE_STATIC (decl))
error_with_decl (decl,
= (args ? TREE_VALUE (args)
: size_int (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
int align;
-
+
/* 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");
case A_FORMAT:
{
- tree format_type = TREE_VALUE (args);
+ 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)));
int format_num;
int first_arg_num;
- int is_scan;
+ enum format_type format_type;
tree argument;
int arg_num;
-
+
if (TREE_CODE (decl) != FUNCTION_DECL)
{
error_with_decl (decl,
continue;
}
- if (TREE_CODE (format_type) == IDENTIFIER_NODE
- && (!strcmp (IDENTIFIER_POINTER (format_type), "printf")
- || !strcmp (IDENTIFIER_POINTER (format_type),
- "__printf__")))
- is_scan = 0;
- else if (TREE_CODE (format_type) == IDENTIFIER_NODE
- && (!strcmp (IDENTIFIER_POINTER (format_type), "scanf")
- || !strcmp (IDENTIFIER_POINTER (format_type),
- "__scanf__")))
- is_scan = 1;
- else if (TREE_CODE (format_type) == IDENTIFIER_NODE)
+ if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
{
- error ("`%s' is an unrecognized format function type",
- IDENTIFIER_POINTER (format_type));
+ error ("unrecognized format specifier");
continue;
}
else
{
- error ("unrecognized format specifier");
- continue;
+ 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
+ {
+ error ("`%s' is an unrecognized format function type", p);
+ continue;
+ }
}
/* Strip any conversions from the string index and first arg number
record_function_format (DECL_NAME (decl),
DECL_ASSEMBLER_NAME (decl),
- is_scan, format_num, first_arg_num);
+ format_type, format_num, first_arg_num);
break;
}
tree format_num_expr = TREE_VALUE (args);
int format_num, 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
"`%s' defined both normally and as an alias");
else if (decl_function_context (decl) == 0)
{
- tree id = get_identifier (TREE_STRING_POINTER
- (TREE_VALUE (args)));
+ 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));
+
if (TREE_CODE (decl) == FUNCTION_DECL)
DECL_INITIAL (decl) = error_mark_node;
else
else
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
break;
+
+ case A_INIT_PRIORITY:
+ {
+ tree initp_expr = (args ? TREE_VALUE (args): NULL_TREE);
+ int pri;
+
+ if (initp_expr)
+ STRIP_NOPS (initp_expr);
+
+ if (!initp_expr || TREE_CODE (initp_expr) != INTEGER_CST)
+ {
+ error ("requested init_priority is not an integer constant");
+ continue;
+ }
+
+ pri = TREE_INT_CST_LOW (initp_expr);
+
+ if (is_type || TREE_CODE (decl) != VAR_DECL
+ || ! TREE_STATIC (decl)
+ || DECL_EXTERNAL (decl)
+ || (TREE_CODE (TREE_TYPE (decl)) != RECORD_TYPE
+ && TREE_CODE (TREE_TYPE (decl)) != UNION_TYPE)
+ /* Static objects in functions are initialized the
+ first time control passes through that
+ function. This is not precise enough to pin down an
+ init_priority value, so don't allow it. */
+ || current_function_decl)
+ {
+ error ("can only use init_priority attribute on file-scope definitions of objects of class type");
+ continue;
+ }
+
+ /* Check for init_priorities that are reserved for
+ implementation. Reserved for language and runtime
+ support implementations.*/
+ if ((10 <= pri && pri <= 99)
+ /* Reserved for standard library implementations. */
+ || (500 <= pri && pri <= 999)
+ /* Reserved for objects with no attributes. */
+ || pri > (MAX_INIT_PRIORITY - 50))
+ {
+ warning
+ ("requested init_priority is reserved for internal use");
+ continue;
+ }
+
+ if (pri > MAX_INIT_PRIORITY || pri <= 0)
+ {
+ error ("requested init_priority is out of range");
+ continue;
+ }
+
+ static_aggregates_initp
+ = perm_tree_cons (initp_expr, decl, static_aggregates_initp);
+ 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;
}
}
}
*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. */
#define T_D &double_type_node
#define T_LD &long_double_type_node
#define T_C &char_type_node
+#define T_UC &unsigned_char_type_node
#define T_V &void_type_node
#define T_W &wchar_type_node
#define T_ST &sizetype
int pointer_count;
/* Type of argument if no length modifier is used. */
tree *nolen;
+ /* Type of argument if length modifier for shortening to byte is used.
+ If NULL, then this modifier is not allowed. */
+ tree *hhlen;
/* Type of argument if length modifier for shortening is used.
If NULL, then this modifier is not allowed. */
tree *hlen;
/* Type of argument if length modifier `L' is used.
If NULL, then this modifier is not allowed. */
tree *bigllen;
+ /* Type of argument if length modifier `Z' is used.
+ If NULL, then this modifier is not allowed. */
+ tree *zlen;
/* List of other modifier characters allowed with these options. */
char *flag_chars;
} format_char_info;
static format_char_info print_char_table[] = {
- { "di", 0, T_I, T_I, T_L, T_LL, T_LL, "-wp0 +" },
- { "oxX", 0, T_UI, T_UI, T_UL, T_ULL, T_ULL, "-wp0#" },
- { "u", 0, T_UI, T_UI, T_UL, T_ULL, T_ULL, "-wp0" },
-/* Two GNU extensions. */
- { "Z", 0, T_ST, NULL, NULL, NULL, NULL, "-wp0" },
- { "m", 0, T_V, NULL, NULL, NULL, NULL, "-wp" },
- { "feEgG", 0, T_D, NULL, NULL, NULL, T_LD, "-wp0 +#" },
- { "c", 0, T_I, NULL, T_W, NULL, NULL, "-w" },
- { "C", 0, T_W, NULL, NULL, NULL, NULL, "-w" },
- { "s", 1, T_C, NULL, T_W, NULL, NULL, "-wp" },
- { "S", 1, T_W, NULL, NULL, NULL, NULL, "-wp" },
- { "p", 1, T_V, NULL, NULL, NULL, NULL, "-w" },
- { "n", 1, T_I, T_S, T_L, T_LL, NULL, "" },
+ { "di", 0, T_I, T_I, T_I, T_L, T_LL, T_LL, T_ST, "-wp0 +" },
+ { "oxX", 0, T_UI, T_UI, T_UI, T_UL, T_ULL, T_ULL, T_ST, "-wp0#" },
+ { "u", 0, T_UI, T_UI, T_UI, T_UL, T_ULL, T_ULL, T_ST, "-wp0" },
+/* A GNU extension. */
+ { "m", 0, T_V, NULL, NULL, NULL, NULL, NULL, NULL, "-wp" },
+ { "feEgGaA", 0, T_D, NULL, NULL, NULL, NULL, T_LD, NULL, "-wp0 +#" },
+ { "c", 0, T_I, NULL, NULL, T_W, NULL, NULL, NULL, "-w" },
+ { "C", 0, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "-w" },
+ { "s", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, "-wp" },
+ { "S", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "-wp" },
+ { "p", 1, T_V, NULL, NULL, NULL, NULL, NULL, NULL, "-w" },
+ { "n", 1, T_I, NULL, T_S, T_L, T_LL, NULL, NULL, "" },
{ NULL }
};
static format_char_info scan_char_table[] = {
- { "di", 1, T_I, T_S, T_L, T_LL, T_LL, "*" },
- { "ouxX", 1, T_UI, T_US, T_UL, T_ULL, T_ULL, "*" },
- { "efgEG", 1, T_F, NULL, T_D, NULL, T_LD, "*" },
- { "sc", 1, T_C, NULL, T_W, NULL, NULL, "*a" },
- { "[", 1, T_C, NULL, NULL, NULL, NULL, "*a" },
- { "C", 1, T_W, NULL, NULL, NULL, NULL, "*" },
- { "S", 1, T_W, NULL, NULL, NULL, NULL, "*" },
- { "p", 2, T_V, NULL, NULL, NULL, NULL, "*" },
- { "n", 1, T_I, T_S, T_L, T_LL, NULL, "" },
+ { "di", 1, T_I, T_C, T_S, T_L, T_LL, T_LL, NULL, "*" },
+ { "ouxX", 1, T_UI, T_UC, T_US, T_UL, T_ULL, T_ULL, NULL, "*" },
+ { "efgEGaA", 1, T_F, NULL, NULL, T_D, NULL, T_LD, NULL, "*" },
+ { "c", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, "*" },
+ { "s", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, "*a" },
+ { "[", 1, T_C, NULL, NULL, NULL, NULL, NULL, NULL, "*a" },
+ { "C", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "*" },
+ { "S", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, "*a" },
+ { "p", 2, T_V, NULL, NULL, NULL, NULL, NULL, NULL, "*" },
+ { "n", 1, T_I, T_C, T_S, T_L, T_LL, NULL, NULL, "" },
+ { NULL }
+};
+
+/* Handle format characters recognized by glibc's strftime.c.
+ '2' - MUST do years as only two digits
+ '3' - MAY do years as only two digits (depending on locale)
+ 'E' - E modifier is acceptable
+ 'O' - O modifier is acceptable to Standard C
+ 'o' - O modifier is acceptable as a GNU extension
+ 'G' - other GNU extensions */
+
+static format_char_info time_char_table[] = {
+ { "y", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
+ { "D", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
+ { "g", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
+ { "cx", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
+ { "%RTXnrt", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "" },
+ { "P", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
+ { "HIMSUWdemw", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
+ { "Vju", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
+ { "Gklsz", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
+ { "ABZa", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
+ { "p", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
+ { "bh", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
+ { "CY", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
{ NULL }
};
struct function_format_info *next; /* next structure on the list */
tree name; /* identifier such as "printf" */
tree assembler_name; /* optional mangled identifier (for C++) */
- int is_scan; /* TRUE if *scanf */
+ 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;
included or not), since it is common to call printf without
including <stdio.h>. There shouldn't be a problem with this,
since ANSI reserves these function names whether you include the
- header file or not. In any case, the checking is harmless.
+ header file or not. In any case, the checking is harmless.
Also initialize the name of function that modify the format string for
internationalization purposes. */
void
init_function_format_info ()
{
- record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2);
- record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3);
- record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3);
- record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2);
- record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3);
- record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3);
- record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
- record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
- record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
+ record_function_format (get_identifier ("printf"), NULL_TREE,
+ printf_format_type, 1, 2);
+ record_function_format (get_identifier ("fprintf"), NULL_TREE,
+ printf_format_type, 2, 3);
+ record_function_format (get_identifier ("sprintf"), NULL_TREE,
+ printf_format_type, 2, 3);
+ record_function_format (get_identifier ("scanf"), NULL_TREE,
+ scanf_format_type, 1, 2);
+ record_function_format (get_identifier ("fscanf"), NULL_TREE,
+ scanf_format_type, 2, 3);
+ record_function_format (get_identifier ("sscanf"), NULL_TREE,
+ scanf_format_type, 2, 3);
+ record_function_format (get_identifier ("vprintf"), NULL_TREE,
+ printf_format_type, 1, 0);
+ record_function_format (get_identifier ("vfprintf"), NULL_TREE,
+ printf_format_type, 2, 0);
+ record_function_format (get_identifier ("vsprintf"), NULL_TREE,
+ printf_format_type, 2, 0);
+ record_function_format (get_identifier ("strftime"), NULL_TREE,
+ strftime_format_type, 3, 0);
record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
/* Record 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). IS_SCAN is true for scanf-type format checking;
- false indicates printf-style format checking. FORMAT_NUM is the number
+ 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). */
-void
-record_function_format (name, assembler_name, is_scan,
+static void
+record_function_format (name, assembler_name, format_type,
format_num, first_arg_num)
tree name;
tree assembler_name;
- int is_scan;
+ enum format_type format_type;
int format_num;
int first_arg_num;
{
info->assembler_name = assembler_name;
}
- info->is_scan = is_scan;
+ info->format_type = format_type;
info->format_num = format_num;
info->first_arg_num = first_arg_num;
}
int i;
int arg_num;
int suppressed, wide, precise;
- int length_char;
+ int length_char = 0;
int format_char;
int format_length;
tree format_tree;
tree wanted_type;
tree first_fillin_param;
char *format_chars;
- format_char_info *fci;
- static char message[132];
+ format_char_info *fci = NULL;
char flag_chars[8];
int has_operand_number = 0;
}
flag_chars[0] = 0;
suppressed = wide = precise = FALSE;
- if (info->is_scan)
+ if (info->format_type == scanf_format_type)
{
suppressed = *format_chars == '*';
if (suppressed)
++format_chars;
- while (isdigit (*format_chars))
+ while (ISDIGIT (*format_chars))
++format_chars;
}
- else
+ else if (info->format_type == strftime_format_type)
+ {
+ while (*format_chars != 0 && index ("_-0^#", *format_chars) != 0)
+ {
+ if (pedantic)
+ warning ("ANSI C does not support the strftime `%c' flag",
+ *format_chars);
+ if (index (flag_chars, *format_chars) != 0)
+ {
+ warning ("repeated `%c' flag in format",
+ *format_chars);
+ ++format_chars;
+ }
+ else
+ {
+ i = strlen (flag_chars);
+ flag_chars[i++] = *format_chars++;
+ flag_chars[i] = 0;
+ }
+ }
+ while (ISDIGIT ((unsigned char) *format_chars))
+ {
+ wide = TRUE;
+ ++format_chars;
+ }
+ if (wide && pedantic)
+ warning ("ANSI C does not support strftime format width");
+ if (*format_chars == 'E' || *format_chars == 'O')
+ {
+ i = strlen (flag_chars);
+ flag_chars[i++] = *format_chars++;
+ flag_chars[i] = 0;
+ if (*format_chars == 'E' || *format_chars == 'O')
+ {
+ warning ("multiple E/O modifiers in format");
+ while (*format_chars == 'E' || *format_chars == 'O')
+ ++format_chars;
+ }
+ }
+ }
+ else if (info->format_type == printf_format_type)
{
/* See if we have a number followed by a dollar sign. If we do,
it is an operand number, so set PARAMS to that operand. */
while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
{
if (index (flag_chars, *format_chars) != 0)
+ warning ("repeated `%c' flag in format", *format_chars++);
+ else
{
- sprintf (message, "repeated `%c' flag in format",
- *format_chars);
- warning (message);
+ i = strlen (flag_chars);
+ flag_chars[i++] = *format_chars++;
+ flag_chars[i] = 0;
}
- i = strlen (flag_chars);
- flag_chars[i++] = *format_chars++;
- flag_chars[i] = 0;
}
- /* "If the space and + flags both appear,
+ /* "If the space and + flags both appear,
the space flag will be ignored." */
if (index (flag_chars, ' ') != 0
&& index (flag_chars, '+') != 0)
&&
(TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
!= unsigned_type_node))
- {
- sprintf (message,
- "field width is not type int (arg %d)",
- arg_num);
- warning (message);
- }
+ warning ("field width is not type int (arg %d)", arg_num);
}
}
else
{
- while (isdigit (*format_chars))
+ while (ISDIGIT (*format_chars))
{
wide = TRUE;
++format_chars;
{
precise = TRUE;
++format_chars;
- if (*format_chars != '*' && !isdigit (*format_chars))
+ if (*format_chars != '*' && !ISDIGIT (*format_chars))
warning ("`.' not followed by `*' or digit in format");
/* "...a...precision...may be indicated by an asterisk.
In this case, an int argument supplies the...precision." */
++arg_num;
if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
!= integer_type_node)
- {
- sprintf (message,
- "field width is not type int (arg %d)",
- arg_num);
- warning (message);
- }
+ warning ("field width is not type int (arg %d)",
+ arg_num);
}
}
else
{
- while (isdigit (*format_chars))
+ while (ISDIGIT (*format_chars))
++format_chars;
}
}
}
- if (*format_chars == 'h' || *format_chars == 'l')
- length_char = *format_chars++;
- else if (*format_chars == 'q' || *format_chars == 'L')
- {
- length_char = *format_chars++;
- if (pedantic)
- pedwarn ("ANSI C does not support the `%c' length modifier",
- length_char);
- }
- else
- length_char = 0;
- if (length_char == 'l' && *format_chars == 'l')
- {
- length_char = 'q', format_chars++;
- if (pedantic)
- pedwarn ("ANSI C does not support the `ll' length modifier");
- }
+
aflag = 0;
- if (*format_chars == 'a')
- {
- aflag = 1;
- format_chars++;
- }
- if (suppressed && length_char != 0)
+
+ if (info->format_type != strftime_format_type)
{
- sprintf (message,
- "use of `*' and `%c' together in format",
- length_char);
- warning (message);
+ if (*format_chars == 'h' || *format_chars == 'l')
+ length_char = *format_chars++;
+ else if (*format_chars == 'q' || *format_chars == 'L')
+ {
+ length_char = *format_chars++;
+ if (pedantic)
+ warning ("ANSI C does not support the `%c' length modifier",
+ length_char);
+ }
+ else if (*format_chars == 'Z')
+ {
+ length_char = *format_chars++;
+ if (pedantic)
+ warning ("ANSI C does not support the `Z' length modifier");
+ }
+ else
+ length_char = 0;
+ if (length_char == 'l' && *format_chars == 'l')
+ {
+ length_char = 'q', format_chars++;
+ /* FIXME: Is allowed in ISO C 9x. */
+ if (pedantic)
+ warning ("ANSI C does not support the `ll' length modifier");
+ }
+ else if (length_char == 'h' && *format_chars == 'h')
+ {
+ length_char = 'H', format_chars++;
+ /* FIXME: Is allowed in ISO C 9x. */
+ if (pedantic)
+ warning ("ANSI C does not support the `hh' length modifier");
+ }
+ if (*format_chars == 'a' && info->format_type == scanf_format_type)
+ {
+ if (format_chars[1] == 's' || format_chars[1] == 'S'
+ || format_chars[1] == '[')
+ {
+ /* `a' is used as a flag. */
+ aflag = 1;
+ format_chars++;
+ }
+ }
+ if (suppressed && length_char != 0)
+ warning ("use of `*' and `%c' together in format", length_char);
}
format_char = *format_chars;
- if (format_char == 0 || format_char == '%')
+ if (format_char == 0
+ || (info->format_type != strftime_format_type && format_char == '%'))
{
warning ("conversion lacks type at end of format");
continue;
}
+ /* The m, C, and S formats are GNU extensions. */
+ if (pedantic && info->format_type != strftime_format_type
+ && (format_char == 'm' || format_char == 'C' || format_char == 'S'))
+ warning ("ANSI C does not support the `%c' format", format_char);
+ /* ??? The a and A formats are C9X extensions, and should be allowed
+ when a C9X option is added. */
+ if (pedantic && info->format_type != strftime_format_type
+ && (format_char == 'a' || format_char == 'A'))
+ warning ("ANSI C does not support the `%c' format", format_char);
format_chars++;
- fci = info->is_scan ? scan_char_table : print_char_table;
+ switch (info->format_type)
+ {
+ case printf_format_type:
+ fci = print_char_table;
+ break;
+ case scanf_format_type:
+ fci = scan_char_table;
+ break;
+ case strftime_format_type:
+ fci = time_char_table;
+ break;
+ default:
+ abort ();
+ }
while (fci->format_chars != 0
&& index (fci->format_chars, format_char) == 0)
++fci;
if (fci->format_chars == 0)
{
if (format_char >= 040 && format_char < 0177)
- sprintf (message,
- "unknown conversion type character `%c' in format",
+ warning ("unknown conversion type character `%c' in format",
format_char);
else
- sprintf (message,
- "unknown conversion type character 0x%x in format",
+ warning ("unknown conversion type character 0x%x in format",
format_char);
- warning (message);
continue;
}
- if (wide && index (fci->flag_chars, 'w') == 0)
+ if (pedantic)
{
- sprintf (message, "width used with `%c' format",
- format_char);
- warning (message);
+ if (index (fci->flag_chars, 'G') != 0)
+ warning ("ANSI C does not support `%%%c'", format_char);
+ if (index (fci->flag_chars, 'o') != 0
+ && index (flag_chars, 'O') != 0)
+ warning ("ANSI C does not support `%%O%c'", format_char);
}
+ if (wide && index (fci->flag_chars, 'w') == 0)
+ warning ("width used with `%c' format", format_char);
+ if (index (fci->flag_chars, '2') != 0)
+ warning ("`%%%c' yields only last 2 digits of year", format_char);
+ else if (index (fci->flag_chars, '3') != 0)
+ warning ("`%%%c' yields only last 2 digits of year in some locales",
+ format_char);
if (precise && index (fci->flag_chars, 'p') == 0)
- {
- sprintf (message, "precision used with `%c' format",
- format_char);
- warning (message);
- }
+ warning ("precision used with `%c' format", format_char);
if (aflag && index (fci->flag_chars, 'a') == 0)
{
- sprintf (message, "`a' flag used with `%c' format",
- format_char);
- warning (message);
+ warning ("`a' flag used with `%c' format", format_char);
+ /* To simplify the following code. */
+ aflag = 0;
}
- if (info->is_scan && format_char == '[')
+ /* The a flag is a GNU extension. */
+ else if (pedantic && aflag)
+ warning ("ANSI C does not support the `a' flag");
+ if (info->format_type == scanf_format_type && format_char == '[')
{
/* Skip over scan set, in case it happens to have '%' in it. */
if (*format_chars == '^')
while (*format_chars && *format_chars != ']')
++format_chars;
if (*format_chars != ']')
- /* The end of the format string was reached. */
- warning ("no closing `]' for `%%[' format");
+ /* The end of the format string was reached. */
+ warning ("no closing `]' for `%%[' format");
}
if (suppressed)
{
if (index (fci->flag_chars, '*') == 0)
- {
- sprintf (message,
- "suppression of `%c' conversion in format",
- format_char);
- warning (message);
- }
+ warning ("suppression of `%c' conversion in format", format_char);
continue;
}
for (i = 0; flag_chars[i] != 0; ++i)
{
if (index (fci->flag_chars, flag_chars[i]) == 0)
- {
- sprintf (message, "flag `%c' used with type `%c'",
- flag_chars[i], format_char);
- warning (message);
- }
+ warning ("flag `%c' used with type `%c'",
+ flag_chars[i], format_char);
}
+ if (info->format_type == strftime_format_type)
+ continue;
if (precise && index (flag_chars, '0') != 0
&& (format_char == 'd' || format_char == 'i'
|| format_char == 'o' || format_char == 'u'
|| format_char == 'x' || format_char == 'x'))
- {
- sprintf (message,
- "`0' flag ignored with precision specifier and `%c' format",
- format_char);
- warning (message);
- }
+ warning ("`0' flag ignored with precision specifier and `%c' format",
+ format_char);
switch (length_char)
{
default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
+ case 'H': wanted_type = fci->hhlen ? *(fci->hhlen) : 0; break;
case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
case 'q': wanted_type = fci->qlen ? *(fci->qlen) : 0; break;
case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
+ case 'Z': wanted_type = fci->zlen ? *fci->zlen : 0; break;
}
if (wanted_type == 0)
- {
- sprintf (message,
- "use of `%c' length character with `%c' type character",
- length_char, format_char);
- warning (message);
- }
-
- /*
- ** XXX -- should kvetch about stuff such as
- ** {
- ** const int i;
- **
- ** scanf ("%d", &i);
- ** }
- */
+ warning ("use of `%c' length character with `%c' type character",
+ length_char, format_char);
/* Finally. . .check type of argument against desired type! */
if (info->first_arg_num == 0)
++arg_num;
cur_type = TREE_TYPE (cur_param);
+ STRIP_NOPS (cur_param);
+
/* Check the types of any additional pointer arguments
that precede the "real" argument. */
- for (i = 0; i < fci->pointer_count; ++i)
+ for (i = 0; i < fci->pointer_count + aflag; ++i)
{
if (TREE_CODE (cur_type) == POINTER_TYPE)
{
cur_type = TREE_TYPE (cur_type);
+
+ if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR)
+ cur_param = TREE_OPERAND (cur_param, 0);
+ else
+ cur_param = 0;
+
continue;
}
if (TREE_CODE (cur_type) != ERROR_MARK)
- {
- sprintf (message,
- "format argument is not a %s (arg %d)",
- ((fci->pointer_count == 1) ? "pointer" : "pointer to a pointer"),
- arg_num);
- warning (message);
- }
+ warning ("format argument is not a %s (arg %d)",
+ ((fci->pointer_count + aflag == 1)
+ ? "pointer" : "pointer to a pointer"),
+ arg_num);
break;
}
+ /* See if this is an attempt to write into a const type with
+ scanf or with printf "%n". */
+ if ((info->format_type == scanf_format_type
+ || (info->format_type == printf_format_type
+ && format_char == 'n'))
+ && i == fci->pointer_count + aflag
+ && wanted_type != 0
+ && TREE_CODE (cur_type) != ERROR_MARK
+ && (TYPE_READONLY (cur_type)
+ || (cur_param != 0
+ && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
+ || (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'd'
+ && TREE_READONLY (cur_param))))))
+ warning ("writing into constant object (arg %d)", arg_num);
+
/* Check the type of the "real" argument, if there's a type we want. */
- if (i == fci->pointer_count && wanted_type != 0
+ if (i == fci->pointer_count + aflag && wanted_type != 0
&& TREE_CODE (cur_type) != ERROR_MARK
&& wanted_type != TYPE_MAIN_VARIANT (cur_type)
/* If we want `void *', allow any pointer type.
{
register char *this;
register char *that;
-
+
this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
that = 0;
if (TREE_CODE (cur_type) != ERROR_MARK
that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
if (strcmp (this, that) != 0)
- {
- sprintf (message, "%s format, %s arg (arg %d)",
- this, that, arg_num);
- warning (message);
- }
+ warning ("%s format, %s arg (arg %d)", this, that, arg_num);
}
}
}
|| TREE_UNSIGNED (type)
|| ! int_fits_type_p (expr, unsigned_type (type)))
&& skip_evaluation == 0)
- warning ("overflow in implicit constant conversion");
+ warning ("overflow in implicit constant conversion");
}
else
unsigned_conversion_warning (t, expr);
if (mode == TYPE_MODE (intDI_type_node))
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+ if (mode == TYPE_MODE (intTI_type_node))
+ return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+
if (mode == TYPE_MODE (float_type_node))
return float_type_node;
binary_op_error (code)
enum tree_code code;
{
- register char *opname = "unknown";
+ register char *opname;
switch (code)
{
case LROTATE_EXPR:
case RROTATE_EXPR:
opname = "rotate"; break;
+ default:
+ opname = "unknown"; break;
}
error ("invalid operands to binary %s", opname);
}
case GE_EXPR:
code = LE_EXPR;
break;
+ default:
+ break;
}
*rescode_ptr = code;
}
primop1 = TYPE_MAX_VALUE (type);
val = 0;
break;
+
+ default:
+ break;
}
type = unsigned_type (type);
}
primop0))))
warning ("unsigned value < 0 is always 0");
value = boolean_false_node;
+ break;
+
+ default:
+ break;
}
if (value != 0)
truthvalue_conversion (TREE_OPERAND (expr, 0)));
else
return truthvalue_conversion (TREE_OPERAND (expr, 0));
-
+
case COND_EXPR:
/* Distribute the conversion into the arms of a COND_EXPR. */
return fold (build (COND_EXPR, boolean_type_node, TREE_OPERAND (expr, 0),
if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR)
warning ("suggest parentheses around assignment used as truth value");
break;
+
+ default:
+ break;
}
if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
- return (build_binary_op
- ((TREE_SIDE_EFFECTS (expr)
- ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
- truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)),
- truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)),
- 0));
+ {
+ tree tem = save_expr (expr);
+ return (build_binary_op
+ ((TREE_SIDE_EFFECTS (expr)
+ ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
+ truthvalue_conversion (build_unary_op (REALPART_EXPR, tem, 0)),
+ truthvalue_conversion (build_unary_op (IMAGPART_EXPR, tem, 0)),
+ 0));
+ }
return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
}
\f
+#if USE_CPPLIB
+/* Read the rest of a #-directive from input stream FINPUT.
+ In normal use, the directive name and the white space after it
+ have already been read, so they won't be included in the result.
+ We allow for the fact that the directive line may contain
+ a newline embedded within a character or string literal which forms
+ a part of the directive.
+
+ The value is a string in a reusable buffer. It remains valid
+ only until the next time this function is called. */
+unsigned char *yy_cur, *yy_lim;
+
+#define GETC() (yy_cur < yy_lim ? *yy_cur++ : yy_get_token ())
+#define UNGETC(c) ((c), yy_cur--)
+
+int
+yy_get_token ()
+{
+ for (;;)
+ {
+ parse_in.limit = parse_in.token_buffer;
+ cpp_token = cpp_get_token (&parse_in);
+ if (cpp_token == CPP_EOF)
+ return -1;
+ yy_lim = CPP_PWRITTEN (&parse_in);
+ yy_cur = parse_in.token_buffer;
+ if (yy_cur < yy_lim)
+ return *yy_cur++;
+ }
+}
+
+char *
+get_directive_line ()
+{
+ static char *directive_buffer = NULL;
+ static unsigned buffer_length = 0;
+ register char *p;
+ register char *buffer_limit;
+ register int looking_for = 0;
+ register int char_escaped = 0;
+
+ if (buffer_length == 0)
+ {
+ directive_buffer = (char *)xmalloc (128);
+ buffer_length = 128;
+ }
+
+ buffer_limit = &directive_buffer[buffer_length];
+
+ for (p = directive_buffer; ; )
+ {
+ int c;
+
+ /* Make buffer bigger if it is full. */
+ if (p >= buffer_limit)
+ {
+ register unsigned bytes_used = (p - directive_buffer);
+
+ buffer_length *= 2;
+ directive_buffer
+ = (char *)xrealloc (directive_buffer, buffer_length);
+ p = &directive_buffer[bytes_used];
+ buffer_limit = &directive_buffer[buffer_length];
+ }
+
+ c = GETC ();
+
+ /* Discard initial whitespace. */
+ if ((c == ' ' || c == '\t') && p == directive_buffer)
+ continue;
+
+ /* Detect the end of the directive. */
+ if (c == '\n' && looking_for == 0)
+ {
+ UNGETC (c);
+ c = '\0';
+ }
+
+ *p++ = c;
+
+ if (c == 0)
+ return directive_buffer;
+
+ /* Handle string and character constant syntax. */
+ if (looking_for)
+ {
+ if (looking_for == c && !char_escaped)
+ looking_for = 0; /* Found terminator... stop looking. */
+ }
+ else
+ if (c == '\'' || c == '"')
+ looking_for = c; /* Don't stop buffering until we see another
+ another one of these (or an EOF). */
+
+ /* Handle backslash. */
+ char_escaped = (c == '\\' && ! char_escaped);
+ }
+}
+#else
/* Read the rest of a #-directive from input stream FINPUT.
In normal use, the directive name and the white space after it
have already been read, so they won't be included in the result.
else
if (c == '\'' || c == '"')
looking_for = c; /* Don't stop buffering until we see another
- another one of these (or an EOF). */
+ one of these (or an EOF). */
/* 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. */
int constp, volatilep;
{
if (TREE_CODE (type) == ARRAY_TYPE)
- {
- tree real_main_variant = TYPE_MAIN_VARIANT (type);
-
- push_obstacks (TYPE_OBSTACK (real_main_variant),
- TYPE_OBSTACK (real_main_variant));
- type = build_array_type (c_build_type_variant (TREE_TYPE (type),
- constp, volatilep),
- TYPE_DOMAIN (type));
-
- /* TYPE must be on same obstack as REAL_MAIN_VARIANT. If not,
- make a copy. (TYPE might have come from the hash table and
- REAL_MAIN_VARIANT might be in some function's obstack.) */
+ return build_array_type (c_build_type_variant (TREE_TYPE (type),
+ constp, volatilep),
+ TYPE_DOMAIN (type));
+ return build_type_variant (type, constp, volatilep);
+}
- if (TYPE_OBSTACK (type) != TYPE_OBSTACK (real_main_variant))
- {
- type = copy_node (type);
- TYPE_POINTER_TO (type) = TYPE_REFERENCE_TO (type) = 0;
- }
+/* Return the typed-based alias set for T, which may be an expression
+ or a type. */
- TYPE_MAIN_VARIANT (type) = real_main_variant;
- pop_obstacks ();
+int
+c_get_alias_set (t)
+ tree t;
+{
+ tree type;
+
+ if (t == error_mark_node)
+ return 0;
+
+ type = (TREE_CODE_CLASS (TREE_CODE (t)) == 't')
+ ? t : TREE_TYPE (t);
+
+ if (type == error_mark_node)
+ return 0;
+
+ /* Deal with special cases first; for certain kinds of references
+ we're interested in more than just the type. */
+
+ if (TREE_CODE (t) == BIT_FIELD_REF)
+ /* Perhaps reads and writes to this piece of data alias fields
+ neighboring the bitfield. Perhaps that's impossible. For now,
+ let's just assume that bitfields can alias everything, which is
+ the conservative assumption. */
+ return 0;
+
+ if (TREE_CODE (t) == COMPONENT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
+ /* 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 through it. Even the type-punning allowed here is a
+ GCC extension, albeit a common and useful one; the C standard
+ says that such accesses have implementation-defined behavior. */
+ return 0;
+
+ /* From here on, only the type matters. */
+
+ if (TYPE_ALIAS_SET_KNOWN_P (type))
+ /* If we've already calculated the value, just return it. */
+ return TYPE_ALIAS_SET (type);
+ else if (TYPE_MAIN_VARIANT (type) != type)
+ /* The C standard specifically allows aliasing between
+ cv-qualified variants of types. */
+ TYPE_ALIAS_SET (type) = c_get_alias_set (TYPE_MAIN_VARIANT (type));
+ else if (TREE_CODE (type) == INTEGER_TYPE)
+ {
+ tree signed_variant;
+
+ /* The C standard specifically allows aliasing between signed and
+ unsigned variants of the same type. We treat the signed
+ variant as canonical. */
+ signed_variant = signed_type (type);
+
+ if (signed_variant != type)
+ TYPE_ALIAS_SET (type) = c_get_alias_set (signed_variant);
+ else if (signed_variant == signed_char_type_node)
+ /* The C standard guarantess that any object may be accessed
+ via an lvalue that has character type. We don't have to
+ check for unsigned_char_type_node or char_type_node because
+ we are specifically looking at the signed variant. */
+ TYPE_ALIAS_SET (type) = 0;
}
- return build_type_variant (type, constp, volatilep);
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ /* Anything that can alias one of the array elements can alias
+ the entire array as well. */
+ TYPE_ALIAS_SET (type) = c_get_alias_set (TREE_TYPE (type));
+ else if (TREE_CODE (type) == FUNCTION_TYPE)
+ /* There are no objects of FUNCTION_TYPE, so there's no point in
+ using up an alias set for them. (There are, of course,
+ pointers and references to functions, but that's
+ different.) */
+ TYPE_ALIAS_SET (type) = 0;
+ else if (TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE)
+ /* If TYPE is a struct or union type then we're reading or
+ writing an entire struct. Thus, we don't know anything about
+ aliasing. (In theory, such an access can only alias objects
+ whose type is the same as one of the fields, recursively, but
+ we don't yet make any use of that information.) */
+ TYPE_ALIAS_SET (type) = 0;
+
+ if (!TYPE_ALIAS_SET_KNOWN_P (type))
+ /* TYPE is something we haven't seen before. Put it in a new
+ alias set. */
+ TYPE_ALIAS_SET (type) = new_alias_set ();
+
+ return TYPE_ALIAS_SET (type);
}