OSDN Git Service

* c-decl.c (grokdeclarator): Don't set DECL_EXTERNAL on
authorgeoffk <geoffk@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 1 Nov 2006 04:47:30 +0000 (04:47 +0000)
committergeoffk <geoffk@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 1 Nov 2006 04:47:30 +0000 (04:47 +0000)
inline static functions in c99 mode.

PR 16622
* doc/extend.texi (Inline): Update.
* c-tree.h (struct language_function): Remove field 'extern_inline'.
* c-decl.c (current_extern_inline): Delete.
(pop_scope): Adjust test for an undefined nested function.
Add warning about undeclared inline function.
(diagnose_mismatched_decls): Update comments.  Disallow overriding
of inline functions in a translation unit in C99.  Allow inline
declarations in C99 at any time.
(merge_decls): Boolize variables.  Handle C99 'extern inline'
semantics.
(grokdeclarator): Set DECL_EXTERNAL here for functions.  Handle
C99 inline semantics.
(start_function): Don't clear current_extern_inline.  Don't set
DECL_EXTERNAL.
(c_push_function_context): Don't push current_extern_inline.
(c_pop_function_context): Don't restore current_extern_inline.

PR 11377
* c-typeck.c (build_external_ref): Warn about static variables
used in extern inline functions.
* c-decl.c (start_decl): Warn about static variables declared
in extern inline functions.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@118356 138bc75d-0d04-0410-961f-82ee72b054a4

12 files changed:
gcc/ChangeLog
gcc/c-decl.c
gcc/c-tree.h
gcc/c-typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/inline-10.c
gcc/testsuite/gcc.dg/inline-13.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/inline-14.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/inline-15.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/inline-16.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/inline6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/inline7.c [new file with mode: 0644]

index 65b835a..0b9caa3 100644 (file)
@@ -1,3 +1,32 @@
+2006-10-31  Geoffrey Keating  <geoffk@apple.com>
+
+       * c-decl.c (grokdeclarator): Don't set DECL_EXTERNAL on
+       inline static functions in c99 mode.
+
+       PR 16622
+       * doc/extend.texi (Inline): Update.
+       * c-tree.h (struct language_function): Remove field 'extern_inline'.
+       * c-decl.c (current_extern_inline): Delete.
+       (pop_scope): Adjust test for an undefined nested function.
+       Add warning about undeclared inline function.
+       (diagnose_mismatched_decls): Update comments.  Disallow overriding
+       of inline functions in a translation unit in C99.  Allow inline
+       declarations in C99 at any time.
+       (merge_decls): Boolize variables.  Handle C99 'extern inline'
+       semantics.
+       (grokdeclarator): Set DECL_EXTERNAL here for functions.  Handle
+       C99 inline semantics.
+       (start_function): Don't clear current_extern_inline.  Don't set
+       DECL_EXTERNAL.
+       (c_push_function_context): Don't push current_extern_inline.
+       (c_pop_function_context): Don't restore current_extern_inline.
+
+       PR 11377
+       * c-typeck.c (build_external_ref): Warn about static variables
+       used in extern inline functions.
+       * c-decl.c (start_decl): Warn about static variables declared
+       in extern inline functions.
+
 2006-10-31  Roger Sayle  <roger@eyesopen.com>
 
        PR middle-end/23470
index 7ebb8b9..34709df 100644 (file)
@@ -154,10 +154,6 @@ int current_function_returns_abnormally;
 
 static int warn_about_return_type;
 
-/* Nonzero when starting a function declared `extern inline'.  */
-
-static int current_extern_inline;
-
 /* Nonzero when the current toplevel function contains a declaration
    of a nested function which is never defined.  */
 
@@ -797,11 +793,22 @@ pop_scope (void)
              && DECL_ABSTRACT_ORIGIN (p) != p)
            TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (p)) = 1;
          if (!DECL_EXTERNAL (p)
-             && DECL_INITIAL (p) == 0)
+             && !DECL_INITIAL (p)
+             && scope != file_scope
+             && scope != external_scope)
            {
              error ("nested function %q+D declared but never defined", p);
              undef_nested_function = true;
            }
+         /* C99 6.7.4p6: "a function with external linkage... declared
+            with an inline function specifier ... shall also be defined in the
+            same translation unit."  */
+         else if (DECL_DECLARED_INLINE_P (p)
+                  && TREE_PUBLIC (p)
+                  && !DECL_INITIAL (p)
+                  && flag_isoc99)
+           pedwarn ("inline function %q+D declared but never defined", p);
+
          goto common_symbol;
 
        case VAR_DECL:
@@ -1292,10 +1299,11 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
 
   /* Function declarations can either be 'static' or 'extern' (no
      qualifier is equivalent to 'extern' - C99 6.2.2p5) and therefore
-     can never conflict with each other on account of linkage (6.2.2p4).
-     Multiple definitions are not allowed (6.9p3,5) but GCC permits
-     two definitions if one is 'extern inline' and one is not.  The non-
-     extern-inline definition supersedes the extern-inline definition.  */
+     can never conflict with each other on account of linkage
+     (6.2.2p4).  Multiple definitions are not allowed (6.9p3,5) but
+     gnu89 mode permits two definitions if one is 'extern inline' and
+     one is not.  The non- extern-inline definition supersedes the
+     extern-inline definition.  */
 
   else if (TREE_CODE (newdecl) == FUNCTION_DECL)
     {
@@ -1321,16 +1329,11 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
            {
              /* If both decls are in the same TU and the new declaration
                 isn't overriding an extern inline reject the new decl.
-                When we handle c99 style inline rules we'll want to reject
-                the following:
-
-                DECL_EXTERN_INLINE (olddecl)
-                && !DECL_EXTERN_INLINE (newdecl)
-
-                if they're in the same translation unit. Until we implement
-                the full semantics we accept the construct.  */
-             if (!(DECL_EXTERN_INLINE (olddecl)
-                   && !DECL_EXTERN_INLINE (newdecl))
+                In c99, no overriding is allowed in the same translation
+                unit.  */
+             if ((!DECL_EXTERN_INLINE (olddecl)
+                  || DECL_EXTERN_INLINE (newdecl)
+                  || flag_isoc99)
                  && same_translation_unit_p (newdecl, olddecl))
                {
                  error ("redefinition of %q+D", newdecl);
@@ -1521,9 +1524,13 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
         ??? Should we still warn about this now we have unit-at-a-time
         mode and can get it right?
         Definitely don't complain if the decls are in different translation
-        units.  */
+        units.
+         C99 permits this, so don't warn in that case.  (The function
+         may not be inlined everywhere in function-at-a-time mode, but
+         we still shouldn't warn.)  */
       if (DECL_DECLARED_INLINE_P (newdecl) && !DECL_DECLARED_INLINE_P (olddecl)
-         && same_translation_unit_p (olddecl, newdecl))
+         && same_translation_unit_p (olddecl, newdecl)
+         && ! flag_isoc99)
        {
          if (TREE_USED (olddecl))
            {
@@ -1600,12 +1607,13 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
 static void
 merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
 {
-  int new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL
-                          && DECL_INITIAL (newdecl) != 0);
-  int new_is_prototype = (TREE_CODE (newdecl) == FUNCTION_DECL
-                         && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0);
-  int old_is_prototype = (TREE_CODE (olddecl) == FUNCTION_DECL
-                         && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) != 0);
+  bool new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL
+                           && DECL_INITIAL (newdecl) != 0);
+  bool new_is_prototype = (TREE_CODE (newdecl) == FUNCTION_DECL
+                          && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0);
+  bool old_is_prototype = (TREE_CODE (olddecl) == FUNCTION_DECL
+                          && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) != 0);
+  bool extern_changed = false;
 
   /* For real parm decl following a forward decl, rechain the old decl
      in its new location and clear TREE_ASM_WRITTEN (it's not a
@@ -1752,6 +1760,18 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
        }
     }
 
+   /* In c99, 'extern' declaration before (or after) 'inline' means this
+      function is not DECL_EXTERNAL.  */
+   if (TREE_CODE (newdecl) == FUNCTION_DECL
+       && (DECL_DECLARED_INLINE_P (newdecl) 
+          || DECL_DECLARED_INLINE_P (olddecl))
+       && (!DECL_DECLARED_INLINE_P (newdecl) 
+          || !DECL_DECLARED_INLINE_P (olddecl)
+          || !DECL_EXTERNAL (olddecl))
+       && DECL_EXTERNAL (newdecl)
+       && flag_isoc99)
+     DECL_EXTERNAL (newdecl) = 0;
+
   if (DECL_EXTERNAL (newdecl))
     {
       TREE_STATIC (newdecl) = TREE_STATIC (olddecl);
@@ -1844,6 +1864,8 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
        }
     }
 
+   extern_changed = DECL_EXTERNAL (olddecl) && !DECL_EXTERNAL (newdecl);
+
   /* Copy most of the decl-specific fields of NEWDECL into OLDDECL.
      But preserve OLDDECL's DECL_UID and DECL_CONTEXT.  */
   {
@@ -1886,6 +1908,13 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
          || (TREE_CODE (olddecl) == VAR_DECL
              && TREE_STATIC (olddecl))))
     make_decl_rtl (olddecl);
+
+  /* If we changed a function from DECL_EXTERNAL to !DECL_EXTERNAL,
+     and the definition is coming from the old version, cgraph needs
+     to be called again.  */
+  if (extern_changed && !new_is_definition 
+      && TREE_CODE (olddecl) == FUNCTION_DECL && DECL_INITIAL (olddecl))
+    cgraph_finalize_function (olddecl, false);
 }
 
 /* Handle when a new declaration NEWDECL has the same name as an old
@@ -3282,6 +3311,17 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
     warning (OPT_Wattributes, "inline function %q+D given attribute noinline",
             decl);
 
+  /* C99 6.7.4p3: An inline definition of a function with external
+     linkage shall not contain a definition of a modifiable object
+     with static storage duration...  */
+  if (TREE_CODE (decl) == VAR_DECL
+      && current_scope != file_scope
+      && TREE_STATIC (decl)
+      && DECL_DECLARED_INLINE_P (current_function_decl)
+      && DECL_EXTERNAL (current_function_decl))
+    pedwarn ("%q+D is static but declared in inline function %qD "
+            "which is not static", decl, current_function_decl);
+
   /* Add this decl to the current scope.
      TEM may equal DECL or it may be a previous decl of the same name.  */
   tem = pushdecl (decl);
@@ -4726,8 +4766,15 @@ grokdeclarator (const struct c_declarator *declarator,
           GCC to signify a forward declaration of a nested function.  */
        if (storage_class == csc_auto && current_scope != file_scope)
          DECL_EXTERNAL (decl) = 0;
+       /* In C99, a function which is declared 'inline' with 'extern'
+          is not an external reference (which is confusing).  It
+          means that the later definition of the function must be output
+          in this file, C99 6.7.4p6.  In GNU C89, a function declared
+          'extern inline' is an external reference.  */
+       else if (declspecs->inline_p && storage_class != csc_static)
+         DECL_EXTERNAL (decl) = (storage_class == csc_extern) == !flag_isoc99;
        else
-         DECL_EXTERNAL (decl) = 1;
+         DECL_EXTERNAL (decl) = !initialized;
 
        /* Record absence of global scope for `static' or `auto'.  */
        TREE_PUBLIC (decl)
@@ -4757,11 +4804,7 @@ grokdeclarator (const struct c_declarator *declarator,
               the abstract origin pointing between the declarations,
               which will confuse dwarf2out.  */
            if (initialized)
-             {
-               DECL_INLINE (decl) = 1;
-               if (storage_class == csc_extern)
-                 current_extern_inline = 1;
-             }
+             DECL_INLINE (decl) = 1;
          }
        /* If -finline-functions, assume it can be inlined.  This does
           two things: let the function be deferred until it is actually
@@ -5259,12 +5302,15 @@ start_struct (enum tree_code code, tree name)
            error ("nested redefinition of %<union %E%>", name);
          else
            error ("nested redefinition of %<struct %E%>", name);
+         /* Don't create structures that contain themselves.  */
+         ref = NULL_TREE;
        }
     }
-  else
-    {
-      /* Otherwise create a forward-reference just so the tag is in scope.  */
 
+  /* Otherwise create a forward-reference just so the tag is in scope.  */
+
+  if (ref == NULL_TREE || TREE_CODE (ref) != code)
+    {
       ref = make_node (code);
       pushtag (name, ref);
     }
@@ -5956,7 +6002,6 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
   current_function_returns_null = 0;
   current_function_returns_abnormally = 0;
   warn_about_return_type = 0;
-  current_extern_inline = 0;
   c_switch_stack = NULL;
 
   nstack_se = XOBNEW (&parser_obstack, struct c_label_context_se);
@@ -6108,12 +6153,6 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
     warning (OPT_Wmissing_declarations,
             "%q+D was used with no declaration before its definition", decl1);
 
-  /* This is a definition, not a reference.
-     So normally clear DECL_EXTERNAL.
-     However, `extern inline' acts like a declaration
-     except for defining how to inline.  So set DECL_EXTERNAL in that case.  */
-  DECL_EXTERNAL (decl1) = current_extern_inline;
-
   /* This function exists in static storage.
      (This does not mean `static' in the C sense!)  */
   TREE_STATIC (decl1) = 1;
@@ -6846,7 +6885,6 @@ c_push_function_context (struct function *f)
   p->returns_null = current_function_returns_null;
   p->returns_abnormally = current_function_returns_abnormally;
   p->warn_about_return_type = warn_about_return_type;
-  p->extern_inline = current_extern_inline;
 }
 
 /* Restore the variables used during compilation of a C function.  */
@@ -6875,7 +6913,6 @@ c_pop_function_context (struct function *f)
   current_function_returns_null = p->returns_null;
   current_function_returns_abnormally = p->returns_abnormally;
   warn_about_return_type = p->warn_about_return_type;
-  current_extern_inline = p->extern_inline;
 
   f->language = NULL;
 }
index 4bc4928..5785e1c 100644 (file)
@@ -384,7 +384,6 @@ struct language_function GTY(())
   int returns_null;
   int returns_abnormally;
   int warn_about_return_type;
-  int extern_inline;
 };
 
 /* Save lists of labels used or defined in particular contexts.
index 2534c25..30b4382 100644 (file)
@@ -2109,6 +2109,17 @@ build_external_ref (tree id, int fun, location_t loc)
       if (context != 0 && context != current_function_decl)
        DECL_NONLOCAL (ref) = 1;
     }
+  /* C99 6.7.4p3: An inline definition of a function with external
+     linkage ... shall not contain a reference to an identifier with
+     internal linkage.  */
+  else if (current_function_decl != 0
+          && DECL_DECLARED_INLINE_P (current_function_decl)
+          && DECL_EXTERNAL (current_function_decl)
+          && VAR_OR_FUNCTION_DECL_P (ref)
+          && (TREE_CODE (ref) != VAR_DECL || TREE_STATIC (ref))
+          && ! TREE_PUBLIC (ref))
+    pedwarn ("%H%qD is static but used in inline function %qD "
+            "which is not static", &loc, ref, current_function_decl);
 
   return ref;
 }
index e5457e0..479113b 100644 (file)
@@ -1,3 +1,18 @@
+2006-10-31  Geoffrey Keating  <geoffk@apple.com>
+
+       * gcc.dg/inline-16.c: New.
+
+       PR 16622
+       * gcc.dg/inline-10.c (main): Don't declare 'main' inline without
+       defining it.
+       * gcc.dg/inline-13.c: New.
+       * gcc.dg/inline-14.c: New.
+       * gcc.dg/inline-15.c: New.
+
+       PR 11377
+       * gcc.dg/inline6.c: New.
+       * gcc.dg/inline7.c: New.
+
 2006-10-31  Roger Sayle  <roger@eyesopen.com>
 
        PR middle-end/23470
index ed6851a..f7a7592 100644 (file)
@@ -3,4 +3,4 @@
 /* { dg-do compile } */
 /* { dg-options "-std=gnu99 -ffreestanding -pedantic-errors" } */
 
-inline int main (void);
+inline int main (void) { return 1; }
diff --git a/gcc/testsuite/gcc.dg/inline-13.c b/gcc/testsuite/gcc.dg/inline-13.c
new file mode 100644 (file)
index 0000000..62a898c
--- /dev/null
@@ -0,0 +1,56 @@
+/* Verify basic C99 inline functionality.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c99" } */
+/* { dg-final { scan-assembler-not "dontgenerate" } } */
+/* { dg-final { scan-assembler "func1" } } */
+/* { dg-final { scan-assembler "func2" } } */
+/* { dg-final { scan-assembler "func3" } } */
+/* { dg-final { scan-assembler "func4" } } */
+/* { dg-final { scan-assembler "func5" } } */
+/* { dg-final { scan-assembler "func6" } } */
+/* { dg-final { scan-assembler "func7" } } */
+/* { dg-final { scan-assembler "func8" } } */
+/* { dg-final { scan-assembler "func9" } } */
+
+inline int dontgenerate1 (void)
+{
+  return 1;
+}
+
+inline int dontgenerate2 (void);
+inline int dontgenerate2 (void)
+{
+  return 2;
+}
+
+inline int dontgenerate3 (void)
+{
+  return 3;
+}
+inline int dontgenerate3 (void);
+
+extern inline int func1 (void) { return 1; }
+
+extern inline int func2 (void);
+inline int func2 (void) { return 2; }
+
+inline int func3 (void) { return 3; }
+extern inline int func3 (void);
+
+inline int func4 (void);
+extern inline int func4 (void) { return 4; }
+
+extern inline int func5 (void) { return 5; }
+inline int func5 (void);
+
+extern int func6 (void);
+inline int func6 (void) { return 6; }
+
+inline int func7 (void) { return 7; }
+extern int func7 (void);
+
+inline int func8 (void);
+extern int func8 (void) { return 8; }
+
+extern int func9 (void) { return 9; }
+inline int func9 (void);
diff --git a/gcc/testsuite/gcc.dg/inline-14.c b/gcc/testsuite/gcc.dg/inline-14.c
new file mode 100644 (file)
index 0000000..0987c7c
--- /dev/null
@@ -0,0 +1,23 @@
+/* Check that you can't redefine a C99 inline function.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c99" } */
+
+extern inline int func1 (void)
+{ /* { dg-error "previous definition" } */
+  return 1;
+}
+
+inline int func1 (void)
+{ /* { dg-error "redefinition" } */
+  return 1;
+}
+
+inline int func2 (void)
+{ /* { dg-error "previous definition" } */
+  return 2;
+}
+
+inline int func2 (void)
+{ /* { dg-error "redefinition" } */
+  return 2;
+}
diff --git a/gcc/testsuite/gcc.dg/inline-15.c b/gcc/testsuite/gcc.dg/inline-15.c
new file mode 100644 (file)
index 0000000..7df8af1
--- /dev/null
@@ -0,0 +1,7 @@
+/* Check that an error message is produced when a C99 inline function
+   is never defined.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c99" } */
+
+extern inline int func1 (void); /* { dg-error "never defined" } */
+inline int func2 (void); /* { dg-error "never defined" } */
diff --git a/gcc/testsuite/gcc.dg/inline-16.c b/gcc/testsuite/gcc.dg/inline-16.c
new file mode 100644 (file)
index 0000000..02aa1bf
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do link } */
+/* { dg-options "-std=c99" } */
+
+static inline int
+func1(const volatile void * base, int byteOffset)
+{
+  volatile int *addr = (volatile int *)((int)base + byteOffset);
+  return *addr;
+}
+
+static inline int
+func2(int data)
+{
+    return func1(&data, 0);
+}
+
+int main(int argc, char *argv[]) {
+  int b = func2(argc);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/inline6.c b/gcc/testsuite/gcc.dg/inline6.c
new file mode 100644 (file)
index 0000000..3b9e3e9
--- /dev/null
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu89" } */
+static int i;
+extern int j;
+extern inline int func1 (void) {
+  return i++;  /* { dg-warning "static" } */
+}
+extern inline int func2 (void) {
+  return j++;
+}
+inline int func3 (void) 
+{
+  return i++;
+}
diff --git a/gcc/testsuite/gcc.dg/inline7.c b/gcc/testsuite/gcc.dg/inline7.c
new file mode 100644 (file)
index 0000000..54f3360
--- /dev/null
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu89" } */
+extern inline void func1 (void) {
+  static int i;  /* { dg-warning "static" } */
+}
+inline void func3 (void) 
+{
+  static int i;
+}