In the tests for this patch I did not cover the C++17
function-pointer-conversion row of [except.handle], because it is
very
uncommon in practice and I didn't think it warranted its own test.
Let me know if I should add that and I can resubmit the patch.
Bootstrapped and regtested on x86_64-pc-linux-gnu.
-- >8 --
The analyzer's exception_matches_type_p only treated an exception as
caught when the handler type and exception type were identical, so a
handler catching a base class did not match a thrown derived class.
Add an eh_may_catch_p langhook returning whether a handler of one
type
catches an exception of another per the language's rules. The default
returns false, preserving behavior for frontends without exception
support (such as C). The C++ frontend implements it via
can_convert_eh
(now non-static). This keeps the analyzer language-agnostic and the
C++
catch-matching rules in the frontend.
PR analyzer/119697
gcc/ChangeLog:
* langhooks.h (struct lang_hooks): Add eh_may_catch_p.
* langhooks-def.h (LANG_HOOKS_EH_MAY_CATCH_P): Define as
hook_bool_tree_tree_false.
(LANG_HOOKS_INITIALIZER): Add it.
gcc/cp/ChangeLog:
* except.cc (can_convert_eh): Make non-static.
* cp-tree.h (can_convert_eh): Declare.
* cp-lang.cc (LANG_HOOKS_EH_MAY_CATCH_P): Define as
can_convert_eh.
gcc/analyzer/ChangeLog:
* ops.cc: Include "langhooks.h".
(exception_matches_type_p): Use the eh_may_catch_p langhook;
fix catch/exception argument order.
gcc/testsuite/ChangeLog:
* g++.dg/analyzer/exception-dynamic-spec.C: Remove xfail.
* g++.dg/analyzer/exception-subclass-1.C: Remove xfail.
__analyzer_dump_path in the catch handler.
* g++.dg/analyzer/exception-subclass-2.C: Add
__analyzer_dump_path in the catch handler;
Add __analyzer_eval to interprocedural call.
* g++.dg/analyzer/exception-subclass-3.C: New test.
* g++.dg/analyzer/exception-subclass-4.C: New test.
* g++.dg/analyzer/multiple-inheritance-1.C: New test.
Signed-off-by: Egas Ribeiro <[email protected]>
---
gcc/analyzer/ops.cc | 13 +-
gcc/cp/cp-lang.cc | 2 +
gcc/cp/cp-tree.h | 1 +
gcc/cp/except.cc | 2 +-
gcc/langhooks-def.h | 2 +
gcc/langhooks.h | 4 +
.../g++.dg/analyzer/exception-dynamic-spec.C | 2 +-
.../g++.dg/analyzer/exception-subclass-1.C | 2 +-
.../g++.dg/analyzer/exception-subclass-2.C | 7 +-
.../g++.dg/analyzer/exception-subclass-3.C | 133
++++++++++++++++++
.../g++.dg/analyzer/exception-subclass-4.C | 16 +++
.../g++.dg/analyzer/multiple-inheritance-1.C | 14 ++
12 files changed, 182 insertions(+), 16 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/analyzer/exception-subclass-
3.C
create mode 100644 gcc/testsuite/g++.dg/analyzer/exception-subclass-
4.C
create mode 100644 gcc/testsuite/g++.dg/analyzer/multiple-
inheritance-1.C
diff --git a/gcc/analyzer/ops.cc b/gcc/analyzer/ops.cc
index 898eaf56485..4aac77d0c1e 100644
--- a/gcc/analyzer/ops.cc
+++ b/gcc/analyzer/ops.cc
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
#include "cgraph.h"
#include "text-art/dump.h"
#include "text-art/tree-widget.h"
+#include "langhooks.h"
#include "analyzer/ops.h"
#include "analyzer/call-details.h"
@@ -1950,18 +1951,10 @@ public:
};
static bool
-exception_matches_type_p (tree exception_type,
- tree catch_type)
+exception_matches_type_p (tree handler_type, tree exception_type)
{
- if (catch_type == exception_type)
+ if (lang_hooks.eh_may_catch_p (handler_type, exception_type))
return true;
-
- /* TODO (PR analyzer/119697): we should also handle subclasses
etc;
- see the rules in
https://en.cppreference.com/w/cpp/language/catch
-
- It looks like we should be calling (or emulating)
- can_convert_eh from the C++ FE, but that's specific to the C++
FE. */
-
return false;
}
diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
index c9d02cc7302..f1096fece1e 100644
--- a/gcc/cp/cp-lang.cc
+++ b/gcc/cp/cp-lang.cc
@@ -78,6 +78,8 @@ static const char *cp_get_sarif_source_language
(const char *);
#define LANG_HOOKS_EH_PERSONALITY cp_eh_personality
#undef LANG_HOOKS_EH_RUNTIME_TYPE
#define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type
+#undef LANG_HOOKS_EH_MAY_CATCH_P
+#define LANG_HOOKS_EH_MAY_CATCH_P can_convert_eh
#undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE
#define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE
cxx_enum_underlying_base_type
#undef LANG_HOOKS_PREPROCESS_MAIN_FILE
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8dac8c98b7a..d4b3e7e5756 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7856,6 +7856,7 @@ extern tree
build_exc_ptr (void);
extern tree build_throw (location_t,
tree,
tsubst_flags_t);
extern int nothrow_libfn_p (const_tree);
+extern bool can_convert_eh (tree, tree);
extern void check_handlers (tree);
extern tree finish_noexcept_expr (tree,
tsubst_flags_t);
extern bool expr_noexcept_p (tree,
tsubst_flags_t);
diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc
index c4b5c388f9b..9ec635af74d 100644
--- a/gcc/cp/except.cc
+++ b/gcc/cp/except.cc
@@ -973,7 +973,7 @@ nothrow_libfn_p (const_tree fn)
/* Returns nonzero if an exception of type FROM will be caught by a
handler for type TO, as per [except.handle]. */
-static bool
+bool
can_convert_eh (tree to, tree from)
{
to = non_reference (to);
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index 33a99266187..2980901b93f 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -149,6 +149,7 @@ extern const char *lhd_get_sarif_source_language
(const char *);
#define LANG_HOOKS_INIT_TS lhd_do_nothing
#define LANG_HOOKS_EH_PERSONALITY lhd_gcc_personality
#define LANG_HOOKS_EH_RUNTIME_TYPE lhd_pass_through_t
+#define LANG_HOOKS_EH_MAY_CATCH_P hook_bool_tree_tree_false
#define LANG_HOOKS_EH_PROTECT_CLEANUP_ACTIONS NULL
#define LANG_HOOKS_BLOCK_MAY_FALLTHRU hook_bool_const_tree_true
#define LANG_HOOKS_EH_USE_CXA_END_CLEANUP false
@@ -406,6 +407,7 @@ extern void lhd_end_section (void);
LANG_HOOKS_EXPR_TO_DECL, \
LANG_HOOKS_EH_PERSONALITY, \
LANG_HOOKS_EH_RUNTIME_TYPE, \
+ LANG_HOOKS_EH_MAY_CATCH_P, \
LANG_HOOKS_EH_PROTECT_CLEANUP_ACTIONS, \
LANG_HOOKS_BLOCK_MAY_FALLTHRU, \
LANG_HOOKS_EH_USE_CXA_END_CLEANUP, \
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 546d7ddcdfb..921c1740fd1 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -644,6 +644,10 @@ struct lang_hooks
/* Map a type to a runtime object to match type. */
tree (*eh_runtime_type) (tree);
+ /* Return true if a handler of HANDLER_TYPE can catch an exception
+ of EXCEPTION_TYPE, per the language's exception-matching
rules. */
+ bool (*eh_may_catch_p) (tree handler_type, tree exception_type);
+
/* If non-NULL, this is a function that returns a function decl to
be
executed if an unhandled exception is propagated out of a
cleanup
region. For example, in C++, an exception thrown by a
destructor
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C
b/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C
index 98472037770..efaa447a919 100644
--- a/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C
+++ b/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C
@@ -26,7 +26,7 @@ void test_2 (int flag) throw (io_error) // { dg-
warning "throwing exception of u
// Valid intraprocedural with subclass:
-void test_3 (int flag) throw (io_error) // { dg-bogus "throwing
exception of unexpected type 'file_io_error' from 'test_3'" "PR
analyzer/119697" { xfail *-*-* } }
+void test_3 (int flag) throw (io_error) // { dg-bogus "throwing
exception of unexpected type 'file_io_error' from 'test_3'" }
{
if (flag)
throw file_io_error();
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
b/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
index 79df33021dd..4b4881286af 100644
--- a/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
@@ -13,7 +13,7 @@ int test ()
try {
throw io_error();
} catch (exception &exc) {
- __analyzer_dump_path (); // { dg-message "path" "PR
analyzer/119697" { xfail *-*-* } }
+ __analyzer_dump_path (); // { dg-message "path" }
return -1;
}
__analyzer_dump_path (); // { dg-bogus "path" }
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C
b/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C
index e9fb61753bb..cd1b7ac570c 100644
--- a/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C
@@ -11,15 +11,16 @@ class io_error : public exception
int __analyzer_inner ()
{
try {
- throw io_error();
+ throw io_error ();
} catch (exception &exc) {
+ __analyzer_dump_path (); // { dg-message "path" }
return -1;
}
__analyzer_dump_path (); // { dg-bogus "path" }
return 0;
}
-int test ()
+void test ()
{
- return __analyzer_inner (); // { dg-message "path" "PR
analyzer/119697" { xfail *-*-* } }
+ __analyzer_eval (__analyzer_inner () == -1); /* { dg-warning
"TRUE" } */
}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-subclass-3.C
b/gcc/testsuite/g++.dg/analyzer/exception-subclass-3.C
new file mode 100644
index 00000000000..687e565187f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-3.C
@@ -0,0 +1,133 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct Base {};
+struct Derived : Base {};
+struct Unrelated {};
+
+struct Child : Base {};
+struct Grandchild : Child {};
+
+struct SiblingA : Base {};
+struct SiblingB : Base {};
+
+struct B1 {};
+struct B2 {};
+struct MultiDerived : B1, B2 {};
+
+struct Amb {};
+struct Mid1 : Amb {};
+struct Mid2 : Amb {};
+struct AmbDerived : Mid1, Mid2 {};
+
+struct PrivDerived : private Base {};
+
+void test_unrelated ()
+{
+ try {
+ throw Derived ();
+ }
+ catch (Unrelated &) {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+}
+
+void test_object_vs_pointer ()
+{
+ try {
+ throw Derived ();
+ }
+ catch (Base *) {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+}
+
+void test_wrong_direction ()
+{
+ try {
+ throw Base ();
+ }
+ catch (Derived &) {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+}
+
+void test_pointer_base ()
+{
+ static Derived d;
+ try {
+ throw &d;
+ }
+ catch (Base *) {
+ __analyzer_dump_path (); // { dg-message "path" }
+ }
+}
+
+void test_grandchild ()
+{
+ try {
+ throw Grandchild ();
+ }
+ catch (Base &) {
+ __analyzer_dump_path (); // { dg-message "path" }
+ }
+}
+
+void test_sibling ()
+{
+ try {
+ throw SiblingA ();
+ }
+ catch (SiblingB &) {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+}
+
+void test_multiple_inheritance ()
+{
+ try {
+ throw MultiDerived ();
+ }
+ catch (B1 &) {
+ __analyzer_dump_path (); // { dg-message "path" }
+ }
+}
+
+void test_multiple_inheritance_b2 ()
+{
+ try {
+ throw MultiDerived ();
+ }
+ catch (B2 &) {
+ __analyzer_dump_path (); // { dg-message "path" }
+ }
+}
+
+void test_ambiguous_base ()
+{
+ try {
+ throw AmbDerived ();
+ }
+ catch (Amb &) {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+}
+
+void test_private_base ()
+{
+ try {
+ throw PrivDerived ();
+ }
+ catch (Base &) {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+}
+
+void test_cv_qualified ()
+{
+ try {
+ throw Derived ();
+ }
+ catch (const Base &) {
+ __analyzer_dump_path (); // { dg-message "path" }
+ }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-subclass-4.C
b/gcc/testsuite/g++.dg/analyzer/exception-subclass-4.C
new file mode 100644
index 00000000000..9debe34639e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-4.C
@@ -0,0 +1,16 @@
+// { dg-additional-options "-std=c++11" }
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct Base {};
+struct Derived : Base {};
+
+void test_nullptr ()
+{
+ try {
+ throw nullptr;
+ }
+ catch (Base *) {
+ __analyzer_dump_path (); // { dg-message "path" }
+ }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/multiple-inheritance-1.C
b/gcc/testsuite/g++.dg/analyzer/multiple-inheritance-1.C
new file mode 100644
index 00000000000..8c78d5fe75a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/multiple-inheritance-1.C
@@ -0,0 +1,14 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct B1 { int x; };
+struct B2 { int y; };
+struct MultiDerived : B1, B2 {};
+
+void test_nonfirst_base_field ()
+{
+ MultiDerived d;
+ d.y = 20;
+ B2 *p = &d;
+ __analyzer_eval (p->y == 20); // { dg-warning "TRUE" }
+ __analyzer_eval (p->y == d.y); // { dg-warning "TRUE" }
+}