+2004-09-09 Joseph S. Myers <jsm@polyomino.org.uk>
+
+ * c-tree.h (C_DECL_USED, parser_obstack, in_alignof, in_sizeof,
+ in_typeof, record_maybe_used_decl, pop_maybe_used,
+ c_expr_sizeof_expr, c_expr_sizeof_type): New.
+ * c-decl.c (parser_obstack): New.
+ (c_init_decl_processing): Initialize parser_obstack.
+ (c_write_global_declarations_1): Check for used but undefined
+ static functions.
+ * c-parse.in (%union): Add otype.
+ (save_obstack_position): New.
+ (extdefs): Use it.
+ (unary_expr): Update in_sizeof and in_alignof. Use
+ c_expr_sizeof_expr and c_expr_sizeof_type.
+ (sizeof): Update in_sizeof.
+ (alignof): Update in_alignof.
+ (typeof): Update in_typeof.
+ (typespec_nonreserved_nonattr): Call pop_maybe_used.
+ * c-typeck.c (in_alignof, in_sizeof, in_typeof, struct
+ maybe_used_decl, maybe_used_decls, record_maybe_used_decl,
+ pop_maybe_used, c_expr_sizeof_expr, c_expr_sizeof_type): New.
+ (build_external_ref): Set C_DECL_USED or call
+ record_maybe_used_decl if appropriate.
+ * toplev.c (check_global_declarations): Check TREE_NO_WARNING.
+
2004-09-08 Eric Christopher <echristo@redhat.com>
* builtins.c: Fix prototype for fold_builtin_atan.
static GTY(()) tree current_function_arg_info;
+/* The obstack on which parser and related data structures, which are
+ not live beyond their top-level declaration or definition, are
+ allocated. */
+struct obstack parser_obstack;
+
/* The current statement tree. */
static GTY(()) struct stmt_tree_s c_stmt_tree;
current_function_decl = 0;
+ gcc_obstack_init (&parser_obstack);
+
/* Make the externals scope. */
push_scope ();
external_scope = current_scope;
/* Process the decls in the order they were written. */
for (i = 0, decl = globals; i < len; i++, decl = TREE_CHAIN (decl))
- vec[i] = decl;
+ {
+ vec[i] = decl;
+ /* Check for used but undefined static functions using the C
+ standard's definition of "used", and set TREE_NO_WARNING so
+ that check_global_declarations doesn't repeat the check. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_INITIAL (decl) == 0
+ && DECL_EXTERNAL (decl)
+ && !TREE_PUBLIC (decl)
+ && C_DECL_USED (decl))
+ {
+ pedwarn ("%J%<%F%> used but never defined", decl, decl);
+ TREE_NO_WARNING (decl) = 1;
+ }
+ }
wrapup_global_declarations (vec, len);
check_global_declarations (vec, len);
%start program
-%union {long itype; tree ttype; struct c_expr exprtype; enum tree_code code;
- location_t location; }
+%union {long itype; tree ttype; void *otype; struct c_expr exprtype;
+ enum tree_code code; location_t location; }
/* All identifiers that are not reserved words
and are not declared typedefs in the current block */
%type <itype> setspecs setspecs_fp extension
%type <location> save_location
+
+%type <otype> save_obstack_position
\f
@@ifobjc
/* the Objective-C nonterminals */
can find a valid list of type and sc specs in $0. */
extdefs:
- {$<ttype>$ = NULL_TREE; } extdef
- | extdefs {$<ttype>$ = NULL_TREE; ggc_collect(); } extdef
+ save_obstack_position { $<ttype>$ = NULL_TREE; } extdef
+ { obstack_free (&parser_obstack, $1); }
+ | extdefs save_obstack_position
+ { $<ttype>$ = NULL_TREE; ggc_collect(); } extdef
+ { obstack_free (&parser_obstack, $2); }
;
extdef:
@@end_ifobjc
;
+/* Record the current position of parser_obstack before a
+ declaration to restore it afterwards. */
+save_obstack_position:
+ { $$ = obstack_alloc (&parser_obstack, 0); }
+ ;
+
datadef:
setspecs notype_initdecls ';'
{ if (pedantic)
$$.original_code = ERROR_MARK; }
| sizeof unary_expr %prec UNARY
{ skip_evaluation--;
+ in_sizeof--;
if (TREE_CODE ($2.value) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND ($2.value, 1)))
error ("`sizeof' applied to a bit-field");
- $$.value = c_sizeof (TREE_TYPE ($2.value));
- $$.original_code = ERROR_MARK; }
+ $$ = c_expr_sizeof_expr ($2); }
| sizeof '(' typename ')' %prec HYPERUNARY
{ skip_evaluation--;
- $$.value = c_sizeof (groktypename ($3));
- $$.original_code = ERROR_MARK; }
+ in_sizeof--;
+ $$ = c_expr_sizeof_type ($3); }
| alignof unary_expr %prec UNARY
{ skip_evaluation--;
+ in_alignof--;
$$.value = c_alignof_expr ($2.value);
$$.original_code = ERROR_MARK; }
| alignof '(' typename ')' %prec HYPERUNARY
{ skip_evaluation--;
+ in_alignof--;
$$.value = c_alignof (groktypename ($3));
$$.original_code = ERROR_MARK; }
| REALPART cast_expr %prec UNARY
;
sizeof:
- SIZEOF { skip_evaluation++; }
+ SIZEOF { skip_evaluation++; in_sizeof++; }
;
alignof:
- ALIGNOF { skip_evaluation++; }
+ ALIGNOF { skip_evaluation++; in_alignof++; }
;
typeof:
- TYPEOF { skip_evaluation++; }
+ TYPEOF { skip_evaluation++; in_typeof++; }
;
cast_expr:
@@end_ifobjc
| typeof '(' expr ')'
{ skip_evaluation--;
+ in_typeof--;
if (TREE_CODE ($3.value) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND ($3.value, 1)))
error ("`typeof' applied to a bit-field");
- $$ = TREE_TYPE ($3.value); }
+ $$ = TREE_TYPE ($3.value);
+ pop_maybe_used (variably_modified_type_p ($$, NULL_TREE)); }
| typeof '(' typename ')'
- { skip_evaluation--; $$ = groktypename ($3); }
+ { skip_evaluation--; in_typeof--; $$ = groktypename ($3);
+ pop_maybe_used (variably_modified_type_p ($$, NULL_TREE)); }
;
/* typespec_nonreserved_attr does not exist. */
they may differ for structures with volatile fields. */
#define C_DECL_REGISTER(EXP) DECL_LANG_FLAG_4 (EXP)
+/* Record whether a decl was used in an expression anywhere except an
+ unevaluated operand of sizeof / typeof / alignof. This is only
+ used for functions declared static but not defined, though outside
+ sizeof and typeof it is set for other function decls as well. */
+#define C_DECL_USED(EXP) DECL_LANG_FLAG_5 (EXP)
+
/* Nonzero for a decl which either doesn't exist or isn't a prototype.
N.B. Could be simplified if all built-in decls had complete prototypes
(but this is presently difficult because some of them need FILE*). */
extern void gen_aux_info_record (tree, int, int, int);
/* in c-decl.c */
+extern struct obstack parser_obstack;
extern tree c_break_label;
extern tree c_cont_label;
((VOLATILE_P) ? TYPE_QUAL_VOLATILE : 0))
/* in c-typeck.c */
+extern int in_alignof;
+extern int in_sizeof;
+extern int in_typeof;
+
extern struct c_switch *c_switch_stack;
extern tree require_complete_type (tree);
extern tree build_indirect_ref (tree, const char *);
extern tree build_array_ref (tree, tree);
extern tree build_external_ref (tree, int);
+extern void record_maybe_used_decl (tree);
+extern void pop_maybe_used (bool);
+extern struct c_expr c_expr_sizeof_expr (struct c_expr);
+extern struct c_expr c_expr_sizeof_type (tree);
extern struct c_expr parser_build_binary_op (enum tree_code, struct c_expr,
struct c_expr);
extern void readonly_error (tree, const char *);
#include "tree-iterator.h"
#include "tree-gimple.h"
+/* The level of nesting inside "__alignof__". */
+int in_alignof;
+
+/* The level of nesting inside "sizeof". */
+int in_sizeof;
+
+/* The level of nesting inside "typeof". */
+int in_typeof;
/* Nonzero if we've already printed a "missing braces around initializer"
message within this initializer. */
assemble_external (ref);
TREE_USED (ref) = 1;
+ if (TREE_CODE (ref) == FUNCTION_DECL && !in_alignof)
+ {
+ if (!in_sizeof && !in_typeof)
+ C_DECL_USED (ref) = 1;
+ else if (DECL_INITIAL (ref) == 0
+ && DECL_EXTERNAL (ref)
+ && !TREE_PUBLIC (ref))
+ record_maybe_used_decl (ref);
+ }
+
if (TREE_CODE (ref) == CONST_DECL)
{
ref = DECL_INITIAL (ref);
return ref;
}
+/* Record details of decls possibly used inside sizeof or typeof. */
+struct maybe_used_decl
+{
+ /* The decl. */
+ tree decl;
+ /* The level seen at (in_sizeof + in_typeof). */
+ int level;
+ /* The next one at this level or above, or NULL. */
+ struct maybe_used_decl *next;
+};
+
+static struct maybe_used_decl *maybe_used_decls;
+
+/* Record that DECL, an undefined static function reference seen
+ inside sizeof or typeof, might be used if the operand of sizeof is
+ a VLA type or the operand of typeof is a variably modified
+ type. */
+
+void
+record_maybe_used_decl (tree decl)
+{
+ struct maybe_used_decl *t = XOBNEW (&parser_obstack, struct maybe_used_decl);
+ t->decl = decl;
+ t->level = in_sizeof + in_typeof;
+ t->next = maybe_used_decls;
+ maybe_used_decls = t;
+}
+
+/* Pop the stack of decls possibly used inside sizeof or typeof. If
+ USED is false, just discard them. If it is true, mark them used
+ (if no longer inside sizeof or typeof) or move them to the next
+ level up (if still inside sizeof or typeof). */
+
+void
+pop_maybe_used (bool used)
+{
+ struct maybe_used_decl *p = maybe_used_decls;
+ int cur_level = in_sizeof + in_typeof;
+ while (p && p->level > cur_level)
+ {
+ if (used)
+ {
+ if (cur_level == 0)
+ C_DECL_USED (p->decl) = 1;
+ else
+ p->level = cur_level;
+ }
+ p = p->next;
+ }
+ if (!used || cur_level == 0)
+ maybe_used_decls = p;
+}
+
+/* Return the result of sizeof applied to EXPR. */
+
+struct c_expr
+c_expr_sizeof_expr (struct c_expr expr)
+{
+ struct c_expr ret;
+ ret.value = c_sizeof (TREE_TYPE (expr.value));
+ ret.original_code = ERROR_MARK;
+ pop_maybe_used (C_TYPE_VARIABLE_SIZE (TREE_TYPE (expr.value)));
+ return ret;
+}
+
+/* Return the result of sizeof applied to T, a structure for the type
+ name passed to sizeof (rather than the type itself). */
+
+struct c_expr
+c_expr_sizeof_type (tree t)
+{
+ tree type;
+ struct c_expr ret;
+ type = groktypename (t);
+ ret.value = c_sizeof (type);
+ ret.original_code = ERROR_MARK;
+ pop_maybe_used (C_TYPE_VARIABLE_SIZE (type));
+ return ret;
+}
+
/* Build a function call to function FUNCTION with parameters PARAMS.
PARAMS is a list--a chain of TREE_LIST nodes--in which the
TREE_VALUE of each node is a parameter-expression.
+2004-09-09 Joseph S. Myers <jsm@polyomino.org.uk>
+
+ * gcc.dg/c90-static-1.c, gcc.dg/c99-static-1.c,
+ gcc.dg/gnu99-static-1.c: New tests.
+
2004-09-08 Devang Patel <dpatel@apple.com>
* gcc.dg/darwin-ld-20040828-1.c: New test.
--- /dev/null
+/* It is a constraint violation for a static function to be declared
+ but not defined if it is used except in a sizeof expression. The
+ use of the function simply being unevaluated is not enough. */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { dg-do compile } */
+/* { dg-options "-O2 -std=iso9899:1990 -pedantic-errors" } */
+
+/* Constraint violation (trivial case, where function is used). */
+static void f0(void); /* { dg-error "used but never defined" } */
+void g0(void) { f0(); }
+
+/* Constraint violation. */
+static void f1(void); /* { dg-error "used but never defined" } */
+void g1(void) { if (0) { f1(); } }
+
+/* Constraint violation. */
+static int f2(void); /* { dg-error "used but never defined" } */
+void g2(void) { 0 ? f2() : 0; }
+
+/* OK. */
+static int f3(void);
+void g3(void) { sizeof(f3()); }
--- /dev/null
+/* It is a constraint violation for a static function to be declared
+ but not defined if it is used except in a sizeof expression whose
+ result is an integer constant. The use of the function simply
+ being unevaluated is not enough. */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { dg-do compile } */
+/* { dg-options "-O2 -std=iso9899:1999 -pedantic-errors" } */
+
+/* Constraint violation (trivial case, where function is used). */
+static void f0(void); /* { dg-error "used but never defined" } */
+void g0(void) { f0(); }
+
+/* Constraint violation. */
+static void f1(void); /* { dg-error "used but never defined" } */
+void g1(void) { if (0) { f1(); } }
+
+/* Constraint violation. */
+static int f2(void); /* { dg-error "used but never defined" } */
+void g2(void) { 0 ? f2() : 0; }
+
+/* OK. */
+static int f3(void);
+void g3(void) { sizeof(f3()); }
+
+/* OK (VM type, not VLA). */
+static int f4(void);
+void g4(void) { sizeof(int (*)[f4()]); }
+
+/* Constraint violation (VLA). */
+static int f5(void); /* { dg-error "used but never defined" "VLA" { xfail *-*-* } } */
+void g5(void) { sizeof(int [0 ? f5() : 1]); }
+
+/* OK (non-constant sizeof inside constant sizeof). */
+static int f6(void);
+void g6(void) { sizeof(sizeof(int [f6()])); }
--- /dev/null
+/* It is a constraint violation for a static function to be declared
+ but not defined if it is used except in a sizeof expression whose
+ result is an integer constant. In GNU C, we need to consider
+ __typeof__ and __alignof__ as well. __alignof__ always returns a
+ constant, so static functions can always be used therein.
+ __typeof__ evaluates its argument iff it has variably modified
+ type. */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { dg-do compile } */
+/* { dg-options "-O2 -std=gnu99 -pedantic-errors" } */
+
+/* __alignof__, OK. */
+static int f0(void);
+void g0(void) { __alignof__(f0()); }
+
+/* __typeof__ not variably modified, OK. */
+static int f1(void);
+void g1(void) { __typeof__(f1()) x; }
+
+/* __typeof__ variably modified, not OK. */
+static int f2(void); /* { dg-error "used but never defined" } */
+void g2(void) { __typeof__(int [f2()]) x; }
+
+/* __typeof__ variably modified, not OK. */
+static int f3(void); /* { dg-error "used but never defined" } */
+void g3(void) { __typeof__(int (*)[f3()]) x; }
+
+/* Integer sizeof of VM typeof, OK. */
+static int f4(void);
+void g4(void) { sizeof(__typeof__(int (*)[f3()])); }
&& DECL_INITIAL (decl) == 0
&& DECL_EXTERNAL (decl)
&& ! DECL_ARTIFICIAL (decl)
+ && ! TREE_NO_WARNING (decl)
&& ! TREE_PUBLIC (decl)
&& (warn_unused_function
|| TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))))