[gcc r17-2927] analyzer: support exception subclass matching [PR analyzer/119697]
Egas Ribeiro via Gcc-cvs <[email protected]> Tue, 4 Aug 2026 09:05:18 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:672923ae05596d602c16f4bdb7449931c5e81cf5 commit r17-2927-g672923ae05596d602c16f4bdb7449931c5e81cf5 Author: Egas Ribeiro <[email protected]> Date: Fri Jun 19 19:19:51 2026 +0100 analyzer: support exception subclass matching [PR analyzer/119697] 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 exception_matches_type_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 exception_matches_type_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 exception_matches_type_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]> Diff: --- 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(-) diff --git a/gcc/analyzer/ops.cc b/gcc/analyzer/ops.cc index 898eaf56485d..382edf7144ef 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.exception_matches_type_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 c9d02cc73024..60fd114d35c1 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_EXCEPTION_MATCHES_TYPE_P +#define LANG_HOOKS_EXCEPTION_MATCHES_TYPE_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 a8af4d389450..69ce5e2e8e77 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7872,6 +7872,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 c4b5c388f9b8..9ec635af74d4 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 33a99266187c..9546b8f0d003 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_EXCEPTION_MATCHES_TYPE_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_EXCEPTION_MATCHES_TYPE_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 546d7ddcdfb9..5af4efcb86c6 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 (*exception_matches_type_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 984720377704..efaa447a919c 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 79df33021dd3..4b4881286afc 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 e9fb61753bba..cd1b7ac570cb 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 000000000000..687e565187f3 --- /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 000000000000..9debe34639ea --- /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 000000000000..8c78d5fe75a1 --- /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" } +}