/* Subroutines shared by all languages that are variants of C.
- Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
This file is part of GNU CC.
#include <stdio.h>
#include <ctype.h>
+#ifndef WCHAR_TYPE_SIZE
+#ifdef INT_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#else
+#define WCHAR_TYPE_SIZE BITS_PER_WORD
+#endif
+#endif
+
extern struct obstack permanent_obstack;
-enum attrs {A_PACKED, A_NOCOMMON, A_NORETURN, A_CONST, A_T_UNION,
+/* Nonzero means the expression being parsed will never be evaluated.
+ 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_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
- A_UNUSED, A_FORMAT, A_WEAK, A_ALIAS};
+ A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
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_international_format PROTO((tree, tree, int));
/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
}
else
{
- char *kind = "function";
- if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE)
- kind = "method";
/* 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, &kind);
+ printable_name = (*decl_printable_name) (current_function_decl, 2);
}
declare_hidden_char_array ("__FUNCTION__", name);
{
int i;
for (i = 0; i < len; i++)
- ((int *) q)[i] = TREE_STRING_POINTER (t)[i];
+ {
+ if (WCHAR_TYPE_SIZE == HOST_BITS_PER_SHORT)
+ ((short *) q)[i] = TREE_STRING_POINTER (t)[i];
+ else
+ ((int *) q)[i] = TREE_STRING_POINTER (t)[i];
+ }
q += len * wchar_bytes;
}
}
{
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, 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);
}
/* 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. */
+ and declaration modifiers but before the declaration proper. */
void
decl_attributes (node, attributes, prefix_attributes)
if (! valid_machine_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 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;
if (is_type
&& TREE_CODE (type) == UNION_TYPE
&& (decl == 0
- || TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type))))
+ || (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
&& TREE_CODE (TREE_VALUE (args)) == STRING_CST)
{
if (TREE_CODE (decl) == VAR_DECL
- && current_function_decl != NULL_TREE)
+ && 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
case A_ALIGNED:
{
tree align_expr
- = args ? TREE_VALUE (args) : size_int (BIGGEST_ALIGNMENT);
+ = (args ? TREE_VALUE (args)
+ : size_int (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
int align;
/* Strip any NOPs of any kind. */
|| !strcmp (IDENTIFIER_POINTER (format_type),
"__scanf__")))
is_scan = 1;
+ else if (TREE_CODE (format_type) == IDENTIFIER_NODE)
+ {
+ error ("`%s' is an unrecognized format function type",
+ IDENTIFIER_POINTER (format_type));
+ continue;
+ }
else
{
- error ("unrecognized format specifier for `%s'");
+ error ("unrecognized format specifier");
continue;
}
if (first_arg_num != 0)
{
/* Verify that first_arg_num points to the last arg,
- the ... */
+ the ... */
while (argument)
arg_num++, argument = TREE_CHAIN (argument);
if (arg_num != first_arg_num)
break;
}
+ case A_FORMAT_ARG:
+ {
+ 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
+ || 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)
+ {
+ error ("format string has non-constant 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; ; ++arg_num)
+ {
+ if (argument == 0 || arg_num == format_num)
+ break;
+ 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))
+ || (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)
}
}
}
+
+/* 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 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;
+}
\f
/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
a parameter list. */
{ NULL }
};
-typedef struct function_format_info {
+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++) */
static function_format_info *function_format_list = NULL;
-static void check_format_info PROTO((function_format_info *, tree));
+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;
+
+static void check_format_info PROTO((function_format_info *, tree));
/* Initialize the table of functions to perform format checking on.
The ANSI functions are always checked (whether <stdio.h> is
included or not), since it is common to call printf without
including <stdio.h>. There shouldn't be a problem with this,
since ANSI reserves these function names whether you include the
- header file or not. In any case, the checking is harmless. */
+ 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 ("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_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
false indicates printf-style 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 teh format string, or zero if no checking is not be done
+ against the format string, or zero if no checking is not be done
(e.g. for varargs such as vfprintf). */
void
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;
+}
+
static char tfaff[] = "too few arguments for format";
\f
/* Check the argument list of a call to printf, scanf, etc.
params = TREE_CHAIN (params);
if (format_tree == 0)
return;
+
/* We can only check the format if it's a string constant. */
while (TREE_CODE (format_tree) == NOP_EXPR)
format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */
- if (format_tree == null_pointer_node)
+
+ 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 *info;
+
+ for (info = international_format_list; info; info = info->next)
+ if (info->assembler_name
+ ? (info->assembler_name == DECL_ASSEMBLER_NAME (function))
+ : (info->name == DECL_NAME (function)))
+ {
+ tree inner_args;
+ int i;
+
+ for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
+ inner_args != 0;
+ inner_args = TREE_CHAIN (inner_args), i++)
+ if (i == info->format_num)
+ {
+ format_tree = TREE_VALUE (inner_args);
+
+ while (TREE_CODE (format_tree) == NOP_EXPR)
+ format_tree = TREE_OPERAND (format_tree, 0);
+ }
+ }
+ }
+
+ if (integer_zerop (format_tree))
{
warning ("null format string");
return;
}
}
}
- if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'q' ||
- *format_chars == 'L')
+ 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++;
+ {
+ length_char = 'q', format_chars++;
+ if (pedantic)
+ pedwarn ("ANSI C does not support the `ll' length modifier");
+ }
aflag = 0;
if (*format_chars == 'a')
{
warning (message);
}
format_char = *format_chars;
- if (format_char == 0)
+ if (format_char == 0 || format_char == '%')
{
warning ("conversion lacks type at end of format");
continue;
|| format_char == 'x' || format_char == 'x'))
{
sprintf (message,
- "precision and `0' flag not both allowed with `%c' format",
+ "`0' flag ignored with precision specifier and `%c' format",
format_char);
warning (message);
}
cur_type = TREE_TYPE (cur_type);
continue;
}
- sprintf (message,
- "format argument is not a %s (arg %d)",
- ((fci->pointer_count == 1) ? "pointer" : "pointer to a pointer"),
- arg_num);
- warning (message);
+ 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);
+ }
break;
}
/* Check the type of the "real" argument, if there's a type we want. */
if (i == fci->pointer_count && wanted_type != 0
+ && TREE_CODE (cur_type) != ERROR_MARK
&& wanted_type != TYPE_MAIN_VARIANT (cur_type)
/* If we want `void *', allow any pointer type.
(Anything else would already have got a warning.) */
&& TREE_OVERFLOW (value))
{
TREE_OVERFLOW (value) = 0;
- warning ("integer overflow in expression");
+ if (skip_evaluation == 0)
+ warning ("integer overflow in expression");
}
else if ((TREE_CODE (value) == REAL_CST
|| (TREE_CODE (value) == COMPLEX_CST
&& TREE_OVERFLOW (value))
{
TREE_OVERFLOW (value) = 0;
- warning ("floating-pointer overflow in expression");
+ if (skip_evaluation == 0)
+ warning ("floating point overflow in expression");
}
}
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))))
&& TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))))
/* If EXPR fits in the unsigned version of TYPE,
don't warn unless pedantic. */
- if (pedantic
- || TREE_UNSIGNED (type)
- || ! int_fits_type_p (expr, unsigned_type (type)))
- warning ("overflow in implicit constant conversion");
+ if ((pedantic
+ || TREE_UNSIGNED (type)
+ || ! int_fits_type_p (expr, unsigned_type (type)))
+ && skip_evaluation == 0)
+ warning ("overflow in implicit constant conversion");
}
else
unsigned_conversion_warning (t, expr);
return 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 it is weak, so we cannot optimize. */
+ if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (expr, 0))) == 'd'
+ && DECL_EXTERNAL (TREE_OPERAND (expr, 0)))
+ break;
+
if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
return build (COMPOUND_EXPR, boolean_type_node,
TREE_OPERAND (expr, 0), boolean_true_node);
if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
|| TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
break;
- /* fall through... */
+ /* fall through... */
case NOP_EXPR:
/* If this is widening the argument, we can ignore it. */
if (TYPE_PRECISION (TREE_TYPE (expr))
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE)
break;
- /* fall through... */
+ /* fall through... */
case BIT_XOR_EXPR:
/* This and MINUS_EXPR can be changed into a comparison of the
two objects. */
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. */
+ 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)
continue;
/* Detect the end of the directive. */
- if (c == '\n' && looking_for == 0)
+ if (looking_for == 0
+ && (c == '\n' || c == EOF))
{
ungetc (c, finput);
c = '\0';