re PR c++/13699 (Extern "C" routine in different namespaces accepted with different...
authorDodji Seketeli <dseketel@redhat.com>
Wed, 16 Jul 2008 23:44:02 +0000 (23:44 +0000)
committerDodji Seketeli <dodji@gcc.gnu.org>
Wed, 16 Jul 2008 23:44:02 +0000 (01:44 +0200)
2008-07-16  Dodji Seketeli  <dseketel@redhat.com>

PR c++/13699
* gcc/cp/name-lookup.c (lookup_extern_c_fun_binding_in_all_ns):
New function.
(pushdecl_maybe_friend): Check if a redeclaration of extern C function
complies with exception specification constraints.

From-SVN: r137904

gcc/cp/ChangeLog
gcc/cp/name-lookup.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/lookup/extern-c-redecl.C [new file with mode: 0644]

index 62d2335..8f87551 100644 (file)
@@ -1,3 +1,11 @@
+2008-07-16  Dodji Seketeli  <dseketel@redhat.com>
+
+       PR c++/13699
+       * gcc/cp/name-lookup.c (lookup_extern_c_fun_binding_in_all_ns):
+       New function.
+       (pushdecl_maybe_friend): Check if a redeclaration of extern C function
+       complies with exception specification constraints.
+
 2008-07-14  Jason Merrill  <jason@redhat.com>
 
        * lex.c (init_reswords): Always set D_OBJC.
index e42f60a..45899c5 100644 (file)
@@ -50,6 +50,7 @@ static bool qualified_lookup_using_namespace (tree, tree,
                                              struct scope_binding *, int);
 static tree lookup_type_current_level (tree);
 static tree push_using_directive (tree);
+static cxx_binding* lookup_extern_c_fun_binding_in_all_ns (tree);
 
 /* The :: namespace.  */
 
@@ -763,6 +764,48 @@ pushdecl_maybe_friend (tree x, bool is_friend)
            }
        }
 
+      /* If x has C linkage-specification, (extern "C"),
+        lookup its binding, in case it's already bound to an object.
+        The lookup is done in all namespaces.
+        If we find an existing binding, make sure it has the same
+        exception specification as x, otherwise, bail in error [7.5, 7.6].  */
+      if ((TREE_CODE (x) == FUNCTION_DECL)
+         && DECL_EXTERN_C_P (x)
+          /* We should ignore declarations happening in system headers.  */
+         && !DECL_IN_SYSTEM_HEADER (x))
+       {
+         cxx_binding *function_binding =
+             lookup_extern_c_fun_binding_in_all_ns (x);
+         if (function_binding
+              && !DECL_IN_SYSTEM_HEADER (function_binding->value))
+           {
+             tree previous = function_binding->value;
+
+             /* In case either x or previous is declared to throw an exception,
+                make sure both exception speficications are equal.  */
+             if (decls_match (x, previous))
+               {
+                 tree x_exception_spec = NULL_TREE;
+                 tree previous_exception_spec = NULL_TREE;
+
+                 x_exception_spec =
+                               TYPE_RAISES_EXCEPTIONS (TREE_TYPE (x));
+                 previous_exception_spec =
+                               TYPE_RAISES_EXCEPTIONS (TREE_TYPE (previous));
+                 if (!comp_except_specs (previous_exception_spec,
+                                         x_exception_spec,
+                                         true))
+                   {
+                     pedwarn ("declaration of %q#D with C language linkage", x);
+                     pedwarn ("conflicts with previous declaration %q+#D",
+                               previous);
+                     pedwarn ("due to different exception specifications");
+                     POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+                   }
+               }
+           }
+       }
+
       if (TREE_CODE (x) == FUNCTION_DECL || DECL_FUNCTION_TEMPLATE_P (x))
        check_default_args (x);
 
@@ -1831,6 +1874,39 @@ binding_for_name (cxx_scope *scope, tree name)
   return result;
 }
 
+/* Walk through the bindings associated to the name of FUNCTION,
+   and return the first binding that declares a function with a
+   "C" linkage specification, a.k.a 'extern "C"'.
+   This function looks for the binding, regardless of which scope it
+   has been defined in. It basically looks in all the known scopes.
+   Note that this function does not lookup for bindings of builtin functions
+   or for functions declared in system headers.  */
+static cxx_binding*
+lookup_extern_c_fun_binding_in_all_ns (tree function)
+{
+  tree name;
+  cxx_binding *iter;
+
+  gcc_assert (function && TREE_CODE (function) == FUNCTION_DECL);
+
+  name = DECL_NAME (function);
+  gcc_assert (name && TREE_CODE (name) == IDENTIFIER_NODE);
+
+  for (iter = IDENTIFIER_NAMESPACE_BINDINGS (name);
+       iter;
+       iter = iter->previous)
+    {
+      if (iter->value
+         && TREE_CODE (iter->value) == FUNCTION_DECL
+         && DECL_EXTERN_C_P (iter->value)
+         && !DECL_ARTIFICIAL (iter->value))
+       {
+         return iter;
+       }
+    }
+  return NULL;
+}
+
 /* Insert another USING_DECL into the current binding level, returning
    this declaration. If this is a redeclaration, do nothing, and
    return NULL_TREE if this not in namespace scope (in namespace
index 33d94fc..76d1751 100644 (file)
@@ -1,3 +1,8 @@
+2008-07-16  Dodji Seketeli  <dseketel@redhat.com>
+
+       PR c++/13699
+       * g++.dg/lookup/extern-c-redecl.C: New test.
+
 2008-07-15  Richard Guenther  <rguenther@suse.de>
 
        PR middle-end/36369
diff --git a/gcc/testsuite/g++.dg/lookup/extern-c-redecl.C b/gcc/testsuite/g++.dg/lookup/extern-c-redecl.C
new file mode 100644 (file)
index 0000000..31d100a
--- /dev/null
@@ -0,0 +1,11 @@
+// Contributed by Dodji Seketeli <dseketel@redhat.com>
+// Origin: PR c++/13699
+// { dg-do compile }
+
+namespace A {
+    extern "C" void foo_func () throw(); // { dg-error "conflicts" }
+}
+// next line should trigger an error because
+// it conflicts with previous declaration of foo_func (), due to
+// different exception specifications.
+extern "C" void foo_func (); // { dg-error "C language|exception specifications" }