On Sun, 2026-06-14 at 19:41 +0100, Egas Ribeiro wrote:

[CCing the C++ frontend maintainers, for their input]

Hi Egas.  Thanks for the patch.  Various comments inline below...

> 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?

The approach you've got in this patch looks simple, which is good -
assuming that the C++ FE maintainers are OK with exposing this as a
langhook.  One issue we might eventually run into is the LTO case where
the analyzer might be analyzing TUs from multiple languages (e.g. a
mixture of C++ and C).  But given that the analyzer barely supports LTO
right now, I think going with the simple approach for now is OK.

> 
> 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.

I think the ChangeLog entries are the right level of verbosity, but
please do a 2nd iteration of the patch with a proper 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.

I'll take a look there.

> 
> 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" }
> +  }
> +}

Out of curiosity, does g++ issue any kind of warning for this?  I
wonder if it should.

> +
> +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" }
> +  }
> +}

This looks good for a first iteration, but let's have some input from a
C++ frontend reviewer (e.g. Jason).

What level of testing has the patch had?

Hope this is constructive; thanks again for the patch.
Dave

Reply via email to