On Fri, 2026-06-19 at 19:19 +0100, Egas Ribeiro wrote:
> This patch assumes that can_convert_eh isn't altered to cover more of
> [except.handle]. I submitted a patch for c++ as well that changes
> can_convert_eh. If that is accepted, another patch can be submitted
> later removing the redundant cxx_eh_may_catch_p wrapper. This patch
> is
> correct regardless of the outcome of that patch.
> 
> 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
> cxx_eh_may_catch_p, which delegates to can_convert_eh (now non-
> static)
> and additionally handles the nullptr_t case from [except.handle] that
> can_convert_eh does not model. This keeps the analyzer language-
> agnostic
> and the C++ catch-matching rules in the frontend.
> 
> 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 (cxx_eh_may_catch_p): New function.
>       (LANG_HOOKS_EH_MAY_CATCH_P): Define as cxx_eh_may_catch_p.
> 
> 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.

Minor nit: the ChangeLog entries should have references to 
  PR analyzer/119697
within them.

> 
> Signed-off-by: Egas Ribeiro <[email protected]>
> ---

[...snip...]

> 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..ec61789bacf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-3.C
> @@ -0,0 +1,123 @@
> +#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" }
> +  }
> +}

Can you also add a test of catching MultiDerived against B2 & please?  
I don't think we currently have test coverage of the analyzer
reading/writing fields of a parent class with multiple-inheritance for
parents that aren't the first, and IIRC that involves some extra logic
for locating where the parent is stored within the child.  Could you
have a go at adding a test for this please (probably in a fresh
testcase though, as it's only tangentially  related to exceptions).

[...snip...]

Thanks for the updated patch.  Looks good to me from the analyzer
point-of-view, but we're waiting on Jason's opinion on whether he's OK
with exposing the "may catch" logic like this via a langhook.

Jason?

Thanks
Dave

Reply via email to