This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Fix PR c++/13699
- From: Dodji Seketeli <dseketel at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Jason Merrill <jason at redhat dot com>
- Date: Wed, 16 Jul 2008 16:31:16 +0200
- Subject: [PATCH] Fix PR c++/13699
This bug happens in the following case:
namespace ns {
extern "C" void foo () throw ();
}
// Let's redeclare foo() here.
extern "C" void foo ();
The re-eclaration should refer the same function as the one
referred to by the first declaration.
The only problem is that the re-declaration does not have the same
exception specification as the first declaration.
So g++ should spit an error or warning here, and it does not. It
compiles that code snippet fine, in trunk.
References to the relevant parts of the spec are mentioned in the PR.
The idea of the fix is simply to make sure that each extern "C" function
re-declaration has an exception specification that complies with the
previous declarations of that extern "C" function.
The proposed patch in attachment as been regtested on trunk on x86 and
X86_64.
Regards,
Dodji.
gcc/cp/ChangeLog:
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.
gcc/testsuite/ChangeLog:
2008-07-16 Dodji Seketeli <dseketel@redhat.com>
PR c++/13699
* g++.dg/lookup/extern-c-redecl.C: New test.
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index e42f60a..d59c191 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -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,51 @@ 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
+ as well as those that are compiler-generated. */
+ && !DECL_IN_SYSTEM_HEADER (x)
+ && !DECL_ARTIFICIAL (x))
+ {
+ cxx_binding *function_binding =
+ lookup_extern_c_fun_binding_in_all_ns (x);
+ if (function_binding
+ && !DECL_IN_SYSTEM_HEADER (function_binding->value)
+ && !DECL_ARTIFICIAL (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 +1877,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_IS_BUILTIN (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
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
index 0000000..31d100a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/extern-c-redecl.C
@@ -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" }