I was initially implementing a language hook like I specified in my
proposal:
```cpp
void register_language_semantics (const language_semantics *);
const language_semantics *get_language_semantics ();

/* Abstract base class for queries about a language's semantics  */
class language_semantics
{
 public:
  /* Return true if a handler of HANDLER_TYPE can catch an exception
     of EXCEPTION_TYPE, per the language's exception-matching rules.  */
  virtual bool eh_may_catch_p (tree handler_type, tree exception_type) const = 
0;
};
```
However, I think this is better done as a langhook similar to
eh_personality/eh_runtime_type rather than a new ana::language_semantics
interface for two reasons:

Adding this new class would imply we want to add multiple frontend
semantic queries eventually, but realistically there's just this one
since vfunc resolution looks like it'll reuse ipa-devirt rather than
needing a frontend query.

The second reason is where the implementation for each
language_semantics would live. it would have to be somewhere like
cp-gimplify.cc (which isn't ideal), and then the object would have to be
registered somewhere once by each frontend, maybe by adding a single
static instance.

Thoughts on adding lang_hooks.exception_handler_matches_p next to the
other EH hooks? Or would it be better to keep analyzer-specific
semantics out of the general langhook table?

I left the ChangeLog entries and the commit message minimal for this
RFC, if this direction is OK I will submit it again with a complete
commit message.

exception-subclass-1's xfail now passes with the fix as expected,
however exception-subclass-2 still fails; I'll ask about this separately
in this thread.

Here is the general idea of the implementation as a lang hook:

-- >8 --

>From f938d778068537839fc7e50b85869fb624d23b10 Mon Sep 17 00:00:00 2001
From: Egas Ribeiro <[email protected]>
Date: Sun, 14 Jun 2026 18:46:28 +0100
Subject: [PATCH] analyzer: exception subclass matching [PR119697]

        PR analyzer/119697
gcc/ChangeLog:
        * langhooks.h (struct lang_hooks): Add eh_may_catch_p.
        * langhooks-def.h (LANG_HOOKS_EH_MAY_CATCH_P): New.
        (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): New.

gcc/analyzer/ChangeLog:
        * ops.cc (exception_matches_type_p): Use eh_may_catch_p
        langhook.

gcc/testsuite/ChangeLog:
        * g++.dg/analyzer/exception-subclass-1.C: Remove xfail.
        * g++.dg/analyzer/exception-subclass-3.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-subclass-1.C    |  2 +-
 .../g++.dg/analyzer/exception-subclass-3.C    | 46 +++++++++++++++++++
 8 files changed, 60 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/analyzer/exception-subclass-3.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 6df271d5e35..64466dbe41e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7855,6 +7855,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 e1e082ef1ac..4f40a49ae3f 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-subclass-1.C 
b/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
index 79df33021dd..552013957e4 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" "PR analyzer/119697" }
     return -1;
   }
   __analyzer_dump_path (); // { dg-bogus "path" }
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..890cee8b0a5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-3.C
@@ -0,0 +1,46 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct Base {};
+struct Derived : Base {};
+struct Unrelated {};
+
+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" }
+  }
+}
-- 
2.54.0

Reply via email to