From bc4b653be6451211226018f96146a1eaa439b6cd Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Thu, 9 Sep 2004 02:16:16 +0100 Subject: [PATCH] c-tree.h (C_DECL_USED, [...]): New. * 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. testsuite: * gcc.dg/c90-static-1.c, gcc.dg/c99-static-1.c, gcc.dg/gnu99-static-1.c: New tests. From-SVN: r87216 --- gcc/ChangeLog | 25 +++++++ gcc/c-decl.c | 23 ++++++- gcc/c-parse.in | 42 ++++++++---- gcc/c-tree.h | 15 ++++ gcc/c-typeck.c | 98 +++++++++++++++++++++++++++ gcc/testsuite/ChangeLog | 5 ++ gcc/testsuite/gcc.dg/c90-static-1.c | 22 ++++++ gcc/testsuite/gcc.dg/c99-static-1.c | 35 ++++++++++ gcc/testsuite/gcc.dg/gnu99-static-1.c | 30 ++++++++ gcc/toplev.c | 1 + 10 files changed, 282 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/c90-static-1.c create mode 100644 gcc/testsuite/gcc.dg/c99-static-1.c create mode 100644 gcc/testsuite/gcc.dg/gnu99-static-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 200d6f4a7f8..1ac41ec5b8e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +2004-09-09 Joseph S. Myers + + * 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 * builtins.c: Fix prototype for fold_builtin_atan. diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 0ffda691861..5399c175d82 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -109,6 +109,11 @@ static location_t current_function_prototype_locus; 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; @@ -2526,6 +2531,8 @@ c_init_decl_processing (void) current_function_decl = 0; + gcc_obstack_init (&parser_obstack); + /* Make the externals scope. */ push_scope (); external_scope = current_scope; @@ -6936,7 +6943,21 @@ c_write_global_declarations_1 (tree globals) /* 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); diff --git a/gcc/c-parse.in b/gcc/c-parse.in index 9774bcf461d..fff001faa17 100644 --- a/gcc/c-parse.in +++ b/gcc/c-parse.in @@ -99,8 +99,8 @@ do { \ %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 */ @@ -241,6 +241,8 @@ do { \ %type setspecs setspecs_fp extension %type save_location + +%type save_obstack_position @@ifobjc /* the Objective-C nonterminals */ @@ -360,8 +362,11 @@ program: /* empty */ can find a valid list of type and sc specs in $0. */ extdefs: - {$$ = NULL_TREE; } extdef - | extdefs {$$ = NULL_TREE; ggc_collect(); } extdef + save_obstack_position { $$ = NULL_TREE; } extdef + { obstack_free (&parser_obstack, $1); } + | extdefs save_obstack_position + { $$ = NULL_TREE; ggc_collect(); } extdef + { obstack_free (&parser_obstack, $2); } ; extdef: @@ -375,6 +380,12 @@ 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) @@ -506,21 +517,23 @@ unary_expr: $$.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 @@ -532,15 +545,15 @@ unary_expr: ; 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: @@ -1376,12 +1389,15 @@ typespec_nonreserved_nonattr: @@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. */ diff --git a/gcc/c-tree.h b/gcc/c-tree.h index 104fe0c4c7f..e241d4bddea 100644 --- a/gcc/c-tree.h +++ b/gcc/c-tree.h @@ -99,6 +99,12 @@ struct lang_type GTY(()) 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*). */ @@ -151,6 +157,7 @@ extern void c_parse_init (void); 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; @@ -224,6 +231,10 @@ extern void c_initialize_diagnostics (diagnostic_context *); ((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); @@ -238,6 +249,10 @@ extern tree build_component_ref (tree, 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 *); diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index c776c7874bf..9b20152cb5b 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -44,6 +44,14 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #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. */ @@ -1751,6 +1759,16 @@ build_external_ref (tree id, int fun) 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); @@ -1772,6 +1790,86 @@ build_external_ref (tree id, int fun) 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. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 23cd6ff72f9..2a2a32565c0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2004-09-09 Joseph S. Myers + + * 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 * gcc.dg/darwin-ld-20040828-1.c: New test. diff --git a/gcc/testsuite/gcc.dg/c90-static-1.c b/gcc/testsuite/gcc.dg/c90-static-1.c new file mode 100644 index 00000000000..cc6f320c5a2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c90-static-1.c @@ -0,0 +1,22 @@ +/* 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 */ +/* { 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()); } diff --git a/gcc/testsuite/gcc.dg/c99-static-1.c b/gcc/testsuite/gcc.dg/c99-static-1.c new file mode 100644 index 00000000000..3c817c624d4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c99-static-1.c @@ -0,0 +1,35 @@ +/* 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 */ +/* { 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()])); } diff --git a/gcc/testsuite/gcc.dg/gnu99-static-1.c b/gcc/testsuite/gcc.dg/gnu99-static-1.c new file mode 100644 index 00000000000..b600a4b1201 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu99-static-1.c @@ -0,0 +1,30 @@ +/* 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 */ +/* { 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()])); } diff --git a/gcc/toplev.c b/gcc/toplev.c index eb4a0c69027..d83d6c5fe90 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -840,6 +840,7 @@ check_global_declarations (tree *vec, int len) && 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)))) -- 2.30.2