c-tree.h (C_DECL_USED, [...]): New.
authorJoseph Myers <jsm@polyomino.org.uk>
Thu, 9 Sep 2004 01:16:16 +0000 (02:16 +0100)
committerJoseph Myers <jsm28@gcc.gnu.org>
Thu, 9 Sep 2004 01:16:16 +0000 (02:16 +0100)
* 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
gcc/c-decl.c
gcc/c-parse.in
gcc/c-tree.h
gcc/c-typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/c90-static-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c99-static-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu99-static-1.c [new file with mode: 0644]
gcc/toplev.c

index 200d6f4a7f8e6712741d4065aeaba060a4ddebd2..1ac41ec5b8e8a264137202e216f11e526321d66a 100644 (file)
@@ -1,3 +1,28 @@
+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.
index 0ffda691861f55dce8640d7227dc2d6713ee18fb..5399c175d829d547c055cc057819f51d7bc9ace8 100644 (file)
@@ -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);
index 9774bcf461d4655c73446db58b3b927904c89928..fff001faa1735137679579d8d6ee50342ad92e9f 100644 (file)
@@ -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 <itype> setspecs setspecs_fp extension
 
 %type <location> save_location
+
+%type <otype> save_obstack_position
 \f
 @@ifobjc
 /* the Objective-C nonterminals */
@@ -360,8 +362,11 @@ program: /* empty */
  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:
@@ -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.  */
index 104fe0c4c7fd06bf5fb69cea8b1481508ee05f47..e241d4bddeaa6514d9067b73965548117ada8210 100644 (file)
@@ -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 *);
index c776c7874bf826f66aeffd5bfa5e6687dea17c70..9b20152cb5bf8788f4a1ba94516eca7bc8771763 100644 (file)
@@ -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.
index 23cd6ff72f9d22e37e7007317316ae877c2e6a0d..2a2a32565c0816f84109314de3f31166dca4bc0f 100644 (file)
@@ -1,3 +1,8 @@
+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.
diff --git a/gcc/testsuite/gcc.dg/c90-static-1.c b/gcc/testsuite/gcc.dg/c90-static-1.c
new file mode 100644 (file)
index 0000000..cc6f320
--- /dev/null
@@ -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 <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()); }
diff --git a/gcc/testsuite/gcc.dg/c99-static-1.c b/gcc/testsuite/gcc.dg/c99-static-1.c
new file mode 100644 (file)
index 0000000..3c817c6
--- /dev/null
@@ -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 <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()])); }
diff --git a/gcc/testsuite/gcc.dg/gnu99-static-1.c b/gcc/testsuite/gcc.dg/gnu99-static-1.c
new file mode 100644 (file)
index 0000000..b600a4b
--- /dev/null
@@ -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 <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()])); }
index eb4a0c69027481ad4b8641b4466661869f2ca573..d83d6c5fe90da7280492ed33cc504856d2919fae 100644 (file)
@@ -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))))