X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fc-common.c;h=1e2f6c5c0bb7c4cf814b89e7a18abea0234c8622;hb=faaf6e4c6d466e9f63f0f4b92a037d34d437106d;hp=eb6ccafba84ccbb6de53e0ade54e032566f462b3;hpb=cbc838f991ff91cc6757b521d7ed6cec9d1320ea;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/c-common.c b/gcc/c-common.c index eb6ccafba84..1e2f6c5c0bb 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -37,14 +37,19 @@ Boston, MA 02111-1307, USA. */ 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__. */ @@ -263,6 +268,7 @@ 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, 1); @@ -274,6 +280,7 @@ init_attributes () 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); } @@ -281,7 +288,7 @@ init_attributes () /* 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) @@ -321,6 +328,8 @@ 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) @@ -358,6 +367,13 @@ decl_attributes (node, attributes, prefix_attributes) 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; @@ -396,7 +412,8 @@ decl_attributes (node, attributes, prefix_attributes) 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 @@ -485,7 +502,8 @@ decl_attributes (node, attributes, prefix_attributes) && 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 @@ -570,9 +588,15 @@ decl_attributes (node, attributes, prefix_attributes) || !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; } @@ -626,7 +650,7 @@ decl_attributes (node, attributes, prefix_attributes) 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) @@ -643,13 +667,77 @@ decl_attributes (node, attributes, prefix_attributes) 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) @@ -668,6 +756,76 @@ decl_attributes (node, attributes, prefix_attributes) } } } + +/* 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; +} /* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against a parameter list. */ @@ -739,7 +897,8 @@ static format_char_info scan_char_table[] = { { 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++) */ @@ -750,14 +909,27 @@ typedef struct function_format_info { 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 is included or not), since it is common to call printf without including . 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 () @@ -771,6 +943,10 @@ 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 @@ -779,7 +955,7 @@ init_function_format_info () 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 @@ -815,6 +991,43 @@ record_function_format (name, assembler_name, is_scan, 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"; /* Check the argument list of a call to printf, scanf, etc. @@ -885,9 +1098,43 @@ check_format_info (info, params) 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 (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"); @@ -1194,7 +1441,7 @@ check_format_info (info, params) || 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); } @@ -1359,7 +1606,8 @@ overflow_warning (value) && 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 @@ -1367,7 +1615,8 @@ overflow_warning (value) && TREE_OVERFLOW (value)) { TREE_OVERFLOW (value) = 0; - warning ("floating point overflow in expression"); + if (skip_evaluation == 0) + warning ("floating point overflow in expression"); } } @@ -1383,6 +1632,7 @@ unsigned_conversion_warning (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)))) @@ -1418,10 +1668,11 @@ convert_and_check (type, expr) && 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); @@ -2072,6 +2323,12 @@ truthvalue_conversion (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); @@ -2114,7 +2371,7 @@ truthvalue_conversion (expr) 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)) @@ -2128,7 +2385,7 @@ truthvalue_conversion (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. */