aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2025-04-28 18:21:24 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2025-04-28 18:21:24 -0400
commit7a39e0ca0652ff84a31efa3c7d4c7a78d9bb95ae (patch)
treeceb9ec30a36ef8956ab42a59872d426f38adde66 /gcc
parent13f4b12393fa9813b57f5d2dff0753945a2812ce (diff)
downloadgcc-7a39e0ca0652ff84a31efa3c7d4c7a78d9bb95ae.zip
gcc-7a39e0ca0652ff84a31efa3c7d4c7a78d9bb95ae.tar.gz
gcc-7a39e0ca0652ff84a31efa3c7d4c7a78d9bb95ae.tar.bz2
analyzer: initial implementation of exception handling [PR97111]
This patch adds initial support for exception-handling to -fanalyzer, handling eh_dispatch for regions of type ERT_TRY and ERT_ALLOWED_EXCEPTIONS. I haven't managed yet seen eh_dispatch for regions of type ERT_CLEANUP and ERT_MUST_NOT_THROW in the analyzer; with this patch it will ICE if it sees those. Additionally, this patch only checks for exact matches of exception types, rather than supporting subclasses and references. I'm deferring fixing this for now whilst figuring out how best to interact with the C++ type system; I'm tracking it as PR analyzer/119697. The patch adds event classes for throwing and catching exceptions, and seems to generate readable warnings for the kinds of leak that might occur due to trying to manage resources manually and forgetting about exceptions; for example: exception-leak-1.C: In function ‘int test()’: exception-leak-1.C:7:9: warning: leak of ‘ptr’ [CWE-401] [-Wanalyzer-malloc-leak] 7 | throw 42; | ^~ ‘int test()’: events 1-3 5 | void *ptr = __builtin_malloc (1024); | ~~~~~~~~~~~~~~~~~^~~~~~ | | | (1) allocated here 6 | 7 | throw 42; | ~~ | | | (2) throwing exception of type ‘int’ here... | (3) ⚠️ ‘ptr’ leaks here; was allocated at (1) Although dynamic exception specifications are only available in C++14 and earlier, the need to support them meant it seemed relatively easy to add a warning to check them, hence the patch adds a new warning for code paths that throw an exception that doesn't match a dynamic exception specification: -Wanalyzer-throw-of-unexpected-type. gcc/analyzer/ChangeLog: PR analyzer/97111 * analyzer.cc (is_cxa_throw_p): New. (is_cxa_rethrow_p): New. * analyzer.opt (Wanalyzer-throw-of-unexpected-type): New. * analyzer.opt.urls: Regenerate. * call-info.cc (custom_edge_info::create_enode): New. * call-info.h (call_info::print): Drop "final". (call_info::add_events_to_path): Likewise. * checker-event.cc (event_kind_to_string): Add cases for event_kind::catch_, event_kind::throw_, and event_kind::unwind. (explicit_throw_event::print_desc): New. (throw_from_call_to_external_fn_event::print_desc): New. (unwind_event::print_desc): New. * checker-event.h (enum class event_kind): Add catch_, throw_, and unwind. (class catch_cfg_edge_event): New. (class throw_event): New. (class explicit_throw_event): New. (class throw_from_call_to_external_fn_event): New. (class unwind_event): New. * common.h (class eh_dispatch_cfg_superedge): New forward decl. (class eh_dispatch_try_cfg_superedge): New forward decl. (class eh_dispatch_allowed_cfg_superedge): New forward decl. (custom_edge_info::create_enode): New vfunc decl. (is_cxa_throw_p): New decl. (is_cxa_rethrow_p): New decl. * diagnostic-manager.cc (diagnostic_manager::add_events_for_superedge): Special-case edges for eh_dispach_try. (diagnostic_manager::prune_path): Call consolidate_unwind_events. (diagnostic_manager::prune_for_sm_diagnostic): Don't filter the new event_kinds. (diagnostic_manager::consolidate_unwind_events): New. * diagnostic-manager.h (diagnostic_manager::consolidate_unwind_events): New decl. * engine.cc (exploded_node::on_stmt_pre): Handle "__cxa_throw", "__cxa_rethrow", and resx statements. (class throw_custom_edge): New. (class unwind_custom_edge): New. (get_eh_outedge): New. (exploded_graph::unwind_from_exception): New. (exploded_node::on_throw): New. (exploded_node::on_resx): New. (exploded_graph::get_or_create_node): Add "add_to_worklist" param and use it. (exploded_graph::process_node): Use edge_info's create_enode vfunc to create enodes, rather than calling get_or_create_node directly. Ignore CFG edges in the sgraph flagged with EH whilst we're exploring the egraph. (exploded_graph_annotator::print_enode): Handle case exploded_node::status::special. * exploded-graph.h (exploded_node::status): Add value "special". (exploded_node::on_throw): New decl. (exploded_node::on_resx): New decl. (exploded_graph::get_or_create_node): Add optional "add_to_worklist" param. (exploded_graph::unwind_from_exception): New decl. * kf-lang-cp.cc (class kf_cxa_allocate_exception): New. (class kf_cxa_begin_catch): New. (class kf_cxa_end_catch): New. (class throw_of_unexpected_type): New. (class kf_cxa_call_unexpected): New. (register_known_functions_lang_cp): Register known functions "__cxa_allocate_exception", "__cxa_begin_catch", "__cxa_end_catch", and "__cxa_call_unexpected". * kf.cc (class kf_eh_pointer): New. (register_known_functions): Register it for BUILT_IN_EH_POINTER. * region-model.cc: Include "analyzer/function-set.h". (exception_node::operator==): New. (exception_node::dump_to_pp): New. (exception_node::dump): New. (exception_node::to_json): New. (exception_node::make_dump_widget): New. (exception_node::maybe_get_type): New. (exception_node::add_to_reachable_regions): New. (region_model::region_model): Initialize m_thrown_exceptions_stack and m_caught_exceptions_stack. (region_model::operator=): Likewise. (region_model::operator==): Compare them. (region_model::dump_to_pp): Dump exception stacks. (region_model::to_json): Add exception stacks. (region_model::make_dump_widget): Likewise. (class exception_thrown_from_unrecognized_call): New. (get_fns_assumed_not_to_throw): New. (can_throw_p): New. (region_model::check_for_throw_inside_call): New. (region_model::on_call_pre): Call check_for_throw_inside_call on unknown fns or those we don't have a body for. (region_model::maybe_update_for_edge): Handle eh_dispatch_stmt statements. Drop old code that called apply_constraints_for_exception on EDGE_EH edges. (class rejected_eh_dispatch): New. (exception_matches_type_p): New. (matches_any_exception_type_p): New. (region_model::apply_constraints_for_eh_dispatch): New. (region_model::apply_constraints_for_eh_dispatch_try): New. (region_model::apply_constraints_for_eh_dispatch_allowed): New. (region_model::apply_constraints_for_exception): Delete. (region_model::can_merge_with_p): Don't merge models with non-equal exception stacks. (region_model::get_referenced_base_regions): Add regions from exception stacks. * region-model.h (struct exception_node): New. (region_model::push_thrown_exception): New. (region_model::get_current_thrown_exception): New. (region_model::pop_thrown_exception): New. (region_model::push_caught_exception): New. (region_model::get_current_caught_exception): New. (region_model::pop_caught_exception): New. (region_model::apply_constraints_for_eh_dispatch_try): New decl. (region_model::apply_constraints_for_eh_dispatch_allowed) New decl. (region_model::apply_constraints_for_exception): Delete. (region_model::apply_constraints_for_eh_dispatch): New decl. (region_model::check_for_throw_inside_call): New decl. (region_model::m_thrown_exceptions_stack): New field. (region_model::m_caught_exceptions_stack): New field. * supergraph.cc: Include "except.h" and "analyzer/region-model.h". (supergraph::add_cfg_edge): Special-case eh_dispatch edges. (superedge::get_description): Use default_tree_printer. (get_catch): New. (eh_dispatch_cfg_superedge::make): New. (eh_dispatch_cfg_superedge::eh_dispatch_cfg_superedge): New. (eh_dispatch_cfg_superedge::get_eh_status): New. (eh_dispatch_try_cfg_superedge::dump_label_to_pp): New. (eh_dispatch_try_cfg_superedge::apply_constraints): New. (eh_dispatch_allowed_cfg_superedge::eh_dispatch_allowed_cfg_superedge): New. (eh_dispatch_allowed_cfg_superedge::dump_label_to_pp): New. (eh_dispatch_allowed_cfg_superedge::apply_constraints): New. * supergraph.h: Include "except.h". (superedge::dyn_cast_eh_dispatch_cfg_superedge): New vfunc. (superedge::dyn_cast_eh_dispatch_try_cfg_superedge): New vfunc. (superedge::dyn_cast_eh_dispatch_allowed_cfg_superedge): New vfunc. (class eh_dispatch_cfg_superedge): New. (is_a_helper <const eh_dispatch_cfg_superedge *>::test): New. (class eh_dispatch_try_cfg_superedge): New. (is_a_helper <const eh_dispatch_try_cfg_superedge *>::test): New. (class eh_dispatch_allowed_cfg_superedge): New. (is_a_helper <const eh_dispatch_allowed_cfg_superedge *>::test): New. * svalue.cc (svalue::maybe_get_type_from_typeinfo): New. * svalue.h (svalue::maybe_get_type_from_typeinfo): New decl. gcc/ChangeLog: PR analyzer/97111 * doc/invoke.texi: Add -Wanalyzer-throw-of-unexpected-type. * gimple.h (gimple_call_nothrow_p): Make arg const. gcc/testsuite/ChangeLog: PR analyzer/97111 * c-c++-common/analyzer/analyzer-verbosity-2a.c: Add -fno-exceptions. * c-c++-common/analyzer/analyzer-verbosity-3a.c: Likewise. * c-c++-common/analyzer/attr-const-2.c: Add __attribute__((nothrow)). * c-c++-common/analyzer/attr-malloc-4.c: Likewise. * c-c++-common/analyzer/attr-malloc-5.c: Likewise. * c-c++-common/analyzer/attr-malloc-6.c: Add -fno-exceptions. * c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c: Likewise. * c-c++-common/analyzer/attr-malloc-exception.c: New test. * c-c++-common/analyzer/call-summaries-pr107158-2.c: Add -fno-exceptions. * c-c++-common/analyzer/call-summaries-pr107158.c: Likewise. * c-c++-common/analyzer/capacity-2.c: Likewise. * c-c++-common/analyzer/coreutils-sum-pr108666.c: Likewise. * c-c++-common/analyzer/data-model-22.c: Likewise. * c-c++-common/analyzer/data-model-5d.c: Likewise. * c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c: Likewise. * c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c: Likewise. * c-c++-common/analyzer/edges-2.c: Likewise. * c-c++-common/analyzer/fd-2.c: Likewise. * c-c++-common/analyzer/fd-3.c: Likewise. * c-c++-common/analyzer/fd-meaning.c: Likewise. * c-c++-common/analyzer/file-1.c: Likewise. * c-c++-common/analyzer/file-3.c: Likewise. * c-c++-common/analyzer/file-meaning-1.c: Likewise. * c-c++-common/analyzer/infinite-recursion.c: Likewise. * c-c++-common/analyzer/leak-3.c: Likewise. * c-c++-common/analyzer/malloc-dedupe-1.c: Likewise. * c-c++-common/analyzer/malloc-in-loop.c: Likewise. * c-c++-common/analyzer/malloc-many-paths-3.c: Likewise. * c-c++-common/analyzer/malloc-paths-5.c: Likewise. * c-c++-common/analyzer/malloc-paths-7.c: Likewise. * c-c++-common/analyzer/malloc-paths-8.c: Likewise. * c-c++-common/analyzer/malloc-vs-local-1a.c: Likewise. * c-c++-common/analyzer/malloc-vs-local-2.c: Likewise. * c-c++-common/analyzer/malloc-vs-local-3.c: Likewise. * c-c++-common/analyzer/paths-7.c: Likewise. * c-c++-common/analyzer/pr110830.c: Likewise. * c-c++-common/analyzer/pr93032-mztools-simplified.c: Likewise. * c-c++-common/analyzer/pr93355-localealias-feasibility-3.c: Likewise. * c-c++-common/analyzer/pr93355-localealias-simplified.c: Likewise. * c-c++-common/analyzer/pr96650-1-trans.c: Likewise. * c-c++-common/analyzer/pr97072.c: Add __attribute__((nothrow)). * c-c++-common/analyzer/pr98575-1.c: Likewise. * c-c++-common/analyzer/pr99716-1.c: Add -fno-exceptions. * c-c++-common/analyzer/pr99716-2.c: Likewise. * c-c++-common/analyzer/pr99716-3.c: Likewise. * c-c++-common/analyzer/pragma-2.c: Likewise. * c-c++-common/analyzer/rhbz1878600.c: Likewise. * c-c++-common/analyzer/strndup-1.c: Likewise. * c-c++-common/analyzer/write-to-string-literal-4-disabled.c: Likewise. * c-c++-common/analyzer/write-to-string-literal-4.c: Likewise. * c-c++-common/analyzer/write-to-string-literal-5.c: Likewise. * c-c++-common/analyzer/zlib-5.c: Likewise. * g++.dg/analyzer/exception-could-throw-1.C: New test. * g++.dg/analyzer/exception-could-throw-2.C: New test. * g++.dg/analyzer/exception-dynamic-spec.C: New test. * g++.dg/analyzer/exception-leak-1.C: New test. * g++.dg/analyzer/exception-leak-2.C: New test. * g++.dg/analyzer/exception-leak-3.C: New test. * g++.dg/analyzer/exception-leak-4.C: New test. * g++.dg/analyzer/exception-leak-5.C: New test. * g++.dg/analyzer/exception-leak-6.C: New test. * g++.dg/analyzer/exception-nothrow.C: New test. * g++.dg/analyzer/exception-path-1.C: New test. * g++.dg/analyzer/exception-path-catch-all-1.C: New test. * g++.dg/analyzer/exception-path-catch-all-2.C: New test. * g++.dg/analyzer/exception-path-unwind-multiple-2.C: New test. * g++.dg/analyzer/exception-path-unwind-multiple.C: New test. * g++.dg/analyzer/exception-path-unwind-single.C: New test. * g++.dg/analyzer/exception-path-with-cleanups.C: New test. * g++.dg/analyzer/exception-rethrow-1.C: New test. * g++.dg/analyzer/exception-rethrow-2.C: New test. * g++.dg/analyzer/exception-stack-1.C: New test. * g++.dg/analyzer/exception-stack-2.C: New test. * g++.dg/analyzer/exception-subclass-1.C: New test. * g++.dg/analyzer/exception-subclass-2.C: New test. * g++.dg/analyzer/exception-value-1.C: New test. * g++.dg/analyzer/exception-value-2.C: New test. * g++.dg/analyzer/fno-exception.C: New test. * g++.dg/analyzer/pr94028.C: Drop xfail. * g++.dg/analyzer/std-unexpected.C: New test. * g++.dg/coroutines/pr105287.C: Drop dg-excess-errors. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc')
-rw-r--r--gcc/analyzer/analyzer.cc20
-rw-r--r--gcc/analyzer/analyzer.opt4
-rw-r--r--gcc/analyzer/analyzer.opt.urls3
-rw-r--r--gcc/analyzer/call-info.cc12
-rw-r--r--gcc/analyzer/call-info.h4
-rw-r--r--gcc/analyzer/checker-event.cc50
-rw-r--r--gcc/analyzer/checker-event.h116
-rw-r--r--gcc/analyzer/common.h11
-rw-r--r--gcc/analyzer/diagnostic-manager.cc78
-rw-r--r--gcc/analyzer/diagnostic-manager.h1
-rw-r--r--gcc/analyzer/engine.cc388
-rw-r--r--gcc/analyzer/exploded-graph.h20
-rw-r--r--gcc/analyzer/kf-lang-cp.cc166
-rw-r--r--gcc/analyzer/kf.cc23
-rw-r--r--gcc/analyzer/region-model.cc571
-rw-r--r--gcc/analyzer/region-model.h101
-rw-r--r--gcc/analyzer/supergraph.cc222
-rw-r--r--gcc/analyzer/supergraph.h165
-rw-r--r--gcc/analyzer/svalue.cc13
-rw-r--r--gcc/analyzer/svalue.h2
-rw-r--r--gcc/doc/invoke.texi13
-rw-r--r--gcc/gimple.h2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-2a.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-3a.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/attr-const-2.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/attr-malloc-4.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/attr-malloc-5.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/attr-malloc-6.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/attr-malloc-exception.c17
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/capacity-2.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/coreutils-sum-pr108666.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/data-model-22.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/data-model-5d.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/edges-2.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/fd-2.c4
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/fd-3.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/fd-meaning.c5
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/file-1.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/file-3.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/file-meaning-1.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/infinite-recursion.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/leak-3.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-dedupe-1.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-in-loop.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-many-paths-3.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-paths-5.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-paths-7.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-paths-8.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-1a.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-2.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-3.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/paths-7.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr110830.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr93032-mztools-simplified.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-feasibility-3.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-simplified.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr96650-1-trans.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr97072.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr98575-1.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr99716-1.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr99716-2.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pr99716-3.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/pragma-2.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/rhbz1878600.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/strndup-1.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4-disabled.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4.c2
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-5.c1
-rw-r--r--gcc/testsuite/c-c++-common/analyzer/zlib-5.c1
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-could-throw-1.C37
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-could-throw-2.C32
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C62
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-leak-1.C8
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-leak-2.C18
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-leak-3.C12
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-leak-4.C16
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-leak-5.C51
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-leak-6.C22
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-nothrow.C26
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-1.C34
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-1.C16
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-2.C22
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C55
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple.C48
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-unwind-single.C43
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-path-with-cleanups.C27
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-rethrow-1.C13
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-rethrow-2.C25
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-stack-1.C35
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-stack-2.C44
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C21
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C25
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-value-1.C20
-rw-r--r--gcc/testsuite/g++.dg/analyzer/exception-value-2.C36
-rw-r--r--gcc/testsuite/g++.dg/analyzer/fno-exception.C12
-rw-r--r--gcc/testsuite/g++.dg/analyzer/pr94028.C2
-rw-r--r--gcc/testsuite/g++.dg/analyzer/std-unexpected.C9
-rw-r--r--gcc/testsuite/g++.dg/coroutines/pr105287.C2
103 files changed, 2777 insertions, 77 deletions
diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index c2814d2..56cb370 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -449,6 +449,26 @@ is_longjmp_call_p (const gcall &call)
return false;
}
+bool
+is_cxa_throw_p (const gcall &call)
+{
+ tree fndecl = gimple_call_fndecl (&call);
+ if (!fndecl)
+ return false;
+
+ return is_named_call_p (fndecl, "__cxa_throw");
+}
+
+bool
+is_cxa_rethrow_p (const gcall &call)
+{
+ tree fndecl = gimple_call_fndecl (&call);
+ if (!fndecl)
+ return false;
+
+ return is_named_call_p (fndecl, "__cxa_rethrow");
+}
+
/* For a CALL that matched is_special_named_call_p or is_named_call_p for
some name, return a name for the called function suitable for use in
diagnostics (stripping the leading underscores). */
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index d5f82c6..2ca9058 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -222,6 +222,10 @@ Wanalyzer-tainted-size
Common Var(warn_analyzer_tainted_size) Init(1) Warning
Warn about code paths in which an unsanitized value is used as a size.
+Wanalyzer-throw-of-unexpected-type
+Common Var(warn_analyzer_throw_of_unexpected_type) Init(1) Warning
+Warn about code paths in which an exception of unexpected type is thrown.
+
Wanalyzer-undefined-behavior-ptrdiff
Common Var(warn_analyzer_undefined_behavior_ptrdiff) Init(1) Warning
Warn about code paths in which pointer subtraction involves undefined behavior.
diff --git a/gcc/analyzer/analyzer.opt.urls b/gcc/analyzer/analyzer.opt.urls
index 18a0d69..e76e6e5 100644
--- a/gcc/analyzer/analyzer.opt.urls
+++ b/gcc/analyzer/analyzer.opt.urls
@@ -114,6 +114,9 @@ UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-tainted-offset)
Wanalyzer-tainted-size
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-tainted-size)
+Wanalyzer-throw-of-unexpected-type
+UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-throw-of-unexpected-type)
+
Wanalyzer-undefined-behavior-ptrdiff
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-undefined-behavior-ptrdiff)
diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc
index 0e48c16..9a698ef 100644
--- a/gcc/analyzer/call-info.cc
+++ b/gcc/analyzer/call-info.cc
@@ -58,6 +58,18 @@ custom_edge_info::update_state (program_state *state,
return update_model (state->m_region_model, eedge, ctxt);
}
+/* Base implementation of custom_edge_info::create_enode vfunc. */
+
+exploded_node *
+custom_edge_info::create_enode (exploded_graph &eg,
+ const program_point &point,
+ program_state &&state,
+ exploded_node *enode_for_diag,
+ region_model_context *) const
+{
+ return eg.get_or_create_node (point, state, enode_for_diag);
+}
+
/* class call_info : public custom_edge_info. */
/* Implementation of custom_edge_info::print vfunc for call_info. */
diff --git a/gcc/analyzer/call-info.h b/gcc/analyzer/call-info.h
index f61b9be..6548d86 100644
--- a/gcc/analyzer/call-info.h
+++ b/gcc/analyzer/call-info.h
@@ -30,9 +30,9 @@ namespace ana {
class call_info : public custom_edge_info
{
public:
- void print (pretty_printer *pp) const final override;
+ void print (pretty_printer *pp) const override;
void add_events_to_path (checker_path *emission_path,
- const exploded_edge &eedge) const final override;
+ const exploded_edge &eedge) const override;
const gcall &get_call_stmt () const { return m_call_stmt; }
tree get_fndecl () const { return m_fndecl; }
diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc
index 914a6c9..958cdbf 100644
--- a/gcc/analyzer/checker-event.cc
+++ b/gcc/analyzer/checker-event.cc
@@ -73,6 +73,8 @@ event_kind_to_string (enum event_kind ek)
return "start_cfg_edge";
case event_kind::end_cfg_edge:
return "end_cfg_edge";
+ case event_kind::catch_:
+ return "catch";
case event_kind::call_edge:
return "call_edge";
case event_kind::return_edge:
@@ -89,6 +91,10 @@ event_kind_to_string (enum event_kind ek)
return "rewind_from_longjmp";
case event_kind::rewind_to_setjmp:
return "rewind_to_setjmp";
+ case event_kind::throw_:
+ return "throw";
+ case event_kind::unwind:
+ return "unwind";
case event_kind::warning:
return "warning";
}
@@ -1104,6 +1110,50 @@ rewind_to_setjmp_event::prepare_for_emission (checker_path *path,
&m_original_setjmp_event_id);
}
+/* class throw_event : public checker_event. */
+
+/* class explicit_throw_event : public throw_event. */
+void
+explicit_throw_event::print_desc (pretty_printer &pp) const
+{
+ if (m_is_rethrow)
+ {
+ if (m_type)
+ pp_printf (&pp, "rethrowing exception of type %qT here...", m_type);
+ else
+ pp_printf (&pp, "rethrowing exception here...");
+ }
+ else
+ {
+ if (m_type)
+ pp_printf (&pp, "throwing exception of type %qT here...", m_type);
+ else
+ pp_printf (&pp, "throwing exception here...");
+ }
+}
+
+/* class throw_from_call_to_external_fn_event : public throw_event. */
+
+void
+throw_from_call_to_external_fn_event::print_desc (pretty_printer &pp) const
+{
+ if (m_fndecl)
+ pp_printf (&pp, "if %qD throws an exception...", m_fndecl);
+ else
+ pp_printf (&pp, "if the called function throws an exception...");
+}
+
+// class unwind_event : public checker_event
+
+void
+unwind_event::print_desc (pretty_printer &pp) const
+{
+ if (m_num_frames > 1)
+ pp_printf (&pp, "unwinding %i stack frames", m_num_frames);
+ else
+ pp_printf (&pp, "unwinding stack frame");
+}
+
/* class warning_event : public checker_event. */
/* Implementation of diagnostic_event::print_desc vfunc for
diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h
index eb69e0f..f3ab899 100644
--- a/gcc/analyzer/checker-event.h
+++ b/gcc/analyzer/checker-event.h
@@ -40,6 +40,7 @@ enum class event_kind
state_change,
start_cfg_edge,
end_cfg_edge,
+ catch_,
call_edge,
return_edge,
start_consolidated_cfg_edges,
@@ -48,6 +49,8 @@ enum class event_kind
setjmp_,
rewind_from_longjmp,
rewind_to_setjmp,
+ throw_,
+ unwind,
warning
};
@@ -71,6 +74,7 @@ extern const char *event_kind_to_string (enum event_kind ek);
cfg_edge_event
start_cfg_edge_event (event_kind::start_cfg_edge)
end_cfg_edge_event (event_kind::end_cfg_edge)
+ catch_cfg_edge_event (event_kind::catch_cfg_edge)
call_event (event_kind::call_edge)
return_edge (event_kind::return_edge)
start_consolidated_cfg_edges_event (event_kind::start_consolidated_cfg_edges)
@@ -80,6 +84,10 @@ extern const char *event_kind_to_string (enum event_kind ek);
rewind_event
rewind_from_longjmp_event (event_kind::rewind_from_longjmp)
rewind_to_setjmp_event (event_kind::rewind_to_setjmp)
+ throw_event (event_kind:throw_)
+ explicit_throw_event
+ throw_from_call_to_external_fn_event
+ unwind_event (event_kind::unwind)
warning_event (event_kind::warning). */
/* Abstract subclass of diagnostic_event; the base class for use in
@@ -471,6 +479,32 @@ public:
}
};
+/* A concrete event subclass for catching an exception
+ e.g. "...catching 'struct io_error' here". */
+
+class catch_cfg_edge_event : public cfg_edge_event
+{
+public:
+ catch_cfg_edge_event (const exploded_edge &eedge,
+ const event_loc_info &loc_info,
+ tree type)
+ : cfg_edge_event (event_kind::catch_, eedge, loc_info),
+ m_type (type)
+ {
+ }
+
+ void print_desc (pretty_printer &pp) const final override
+ {
+ if (m_type)
+ pp_printf (&pp, "...catching exception of type %qT here", m_type);
+ else
+ pp_string (&pp, "...catching exception here");
+ }
+
+private:
+ tree m_type;
+};
+
/* A concrete event subclass for an interprocedural call. */
class call_event : public superedge_event
@@ -665,6 +699,88 @@ private:
diagnostic_event_id_t m_original_setjmp_event_id;
};
+/* An abstract subclass for throwing/rethrowing an exception. */
+
+class throw_event : public checker_event
+{
+public:
+ throw_event (const event_loc_info &loc_info,
+ const exploded_node *enode,
+ const gcall &throw_call)
+ : checker_event (event_kind::throw_, loc_info),
+ m_enode (enode),
+ m_throw_call (throw_call)
+ {
+ }
+
+protected:
+ const exploded_node *m_enode;
+ const gcall &m_throw_call;
+};
+
+/* A concrete event subclass for an explicit "throw EXC;"
+ or "throw;" (actually, a call to __cxa_throw or __cxa_rethrow). */
+
+class explicit_throw_event : public throw_event
+{
+public:
+ explicit_throw_event (const event_loc_info &loc_info,
+ const exploded_node *enode,
+ const gcall &throw_call,
+ tree type,
+ bool is_rethrow)
+ : throw_event (loc_info, enode, throw_call),
+ m_type (type),
+ m_is_rethrow (is_rethrow)
+ {
+ }
+
+ void print_desc (pretty_printer &pp) const final override;
+
+private:
+ tree m_type;
+ bool m_is_rethrow;
+};
+
+/* A concrete event subclass for an exception being thrown
+ from within a call to a function we don't have the body of,
+ or where we don't know what function was called. */
+
+class throw_from_call_to_external_fn_event : public throw_event
+{
+public:
+ throw_from_call_to_external_fn_event (const event_loc_info &loc_info,
+ const exploded_node *enode,
+ const gcall &throw_call,
+ tree fndecl)
+ : throw_event (loc_info, enode, throw_call),
+ m_fndecl (fndecl)
+ {
+ }
+
+ void print_desc (pretty_printer &pp) const final override;
+
+private:
+ tree m_fndecl;
+};
+
+/* A concrete event subclass for unwinding a stack frame when
+ processing an exception. */
+
+class unwind_event : public checker_event
+{
+public:
+ unwind_event (const event_loc_info &loc_info)
+ : checker_event (event_kind::unwind, loc_info),
+ m_num_frames (1)
+ {
+ }
+
+ void print_desc (pretty_printer &pp) const final override;
+
+ int m_num_frames;
+};
+
/* Concrete subclass of checker_event for use at the end of a path:
a repeat of the warning message at the end of the path (perhaps with
references to pertinent events that occurred on the way), at the point
diff --git a/gcc/analyzer/common.h b/gcc/analyzer/common.h
index 3d02b62..cb03004 100644
--- a/gcc/analyzer/common.h
+++ b/gcc/analyzer/common.h
@@ -49,6 +49,9 @@ class supernode;
class superedge;
class cfg_superedge;
class switch_cfg_superedge;
+ class eh_dispatch_cfg_superedge;
+ class eh_dispatch_try_cfg_superedge;
+ class eh_dispatch_allowed_cfg_superedge;
class callgraph_superedge;
class call_superedge;
class return_superedge;
@@ -396,6 +399,12 @@ public:
virtual void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge) const = 0;
+
+ virtual exploded_node *create_enode (exploded_graph &eg,
+ const program_point &point,
+ program_state &&state,
+ exploded_node *enode_for_diag,
+ region_model_context *ctxt) const;
};
/* Abstract base class for splitting state.
@@ -472,6 +481,8 @@ extern bool is_std_named_call_p (const_tree fndecl, const char *funcname,
extern bool is_setjmp_call_p (const gcall &call);
extern bool is_longjmp_call_p (const gcall &call);
extern bool is_placement_new_p (const gcall &call);
+extern bool is_cxa_throw_p (const gcall &call);
+extern bool is_cxa_rethrow_p (const gcall &call);
extern const char *get_user_facing_name (const gcall &call);
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 161ae62..7575b16 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -2427,6 +2427,35 @@ diagnostic_manager::add_events_for_superedge (const path_builder &pb,
{
case SUPEREDGE_CFG_EDGE:
{
+ if (auto eh_dispatch_try_sedge
+ = eedge.m_sedge->dyn_cast_eh_dispatch_try_cfg_superedge ())
+ {
+ if (eh_dispatch_try_sedge->get_eh_catch ())
+ {
+ const region_model *model = src_node->get_state ().m_region_model;
+ auto curr_thrown_exception_node
+ = model->get_current_thrown_exception ();
+ gcc_assert (curr_thrown_exception_node);
+ tree type = curr_thrown_exception_node->maybe_get_type ();
+ emission_path->add_event
+ (std::make_unique<catch_cfg_edge_event>
+ (eedge,
+ event_loc_info (dst_point.get_supernode ()->get_start_location (),
+ dst_point.get_fndecl (),
+ dst_stack_depth),
+ type));
+ return;
+ }
+ else
+ {
+ /* We have the "uncaught exception" sedge, from eh_dispatch
+ to a block containing resx.
+ Don't add any events for this, so that we can consolidate
+ adjacent stack unwinding events. */
+ return;
+ }
+ }
+
emission_path->add_event
(std::make_unique<start_cfg_edge_event>
(eedge,
@@ -2502,6 +2531,7 @@ diagnostic_manager::prune_path (checker_path *path,
if (! flag_analyzer_show_events_in_system_headers)
prune_system_headers (path);
consolidate_conditions (path);
+ consolidate_unwind_events (path);
finish_pruning (path);
path->maybe_log (get_logger (), "pruned");
}
@@ -2690,6 +2720,12 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
filtered when their start event is filtered. */
break;
+ case event_kind::catch_:
+ case event_kind::throw_:
+ case event_kind::unwind:
+ /* Don't filter these. */
+ break;
+
case event_kind::call_edge:
{
call_event *event = (call_event *)base_event;
@@ -3130,6 +3166,48 @@ diagnostic_manager::consolidate_conditions (checker_path *path) const
}
}
+/* Consolidate runs of consecutive unwind_event. */
+
+void
+diagnostic_manager::consolidate_unwind_events (checker_path *path) const
+{
+ /* Don't simplify edges if we're debugging them. */
+ if (flag_analyzer_verbose_edges)
+ return;
+
+ for (int start_idx = 0;
+ start_idx < (signed)path->num_events () - 1;
+ start_idx++)
+ {
+ /* Find a run of consecutive unwind_event instances. */
+ if (path->get_checker_event (start_idx)->m_kind != event_kind::unwind)
+ continue;
+ int iter_idx = start_idx + 1;
+ while (iter_idx < (int)path->num_events ())
+ if (path->get_checker_event (iter_idx)->m_kind == event_kind::unwind)
+ ++iter_idx;
+ else
+ break;
+
+ /* iter_idx should now be one after the last unwind_event in the run. */
+ const int last_idx = iter_idx - 1;
+ if (last_idx == start_idx)
+ continue;
+
+ gcc_assert (last_idx > start_idx);
+
+ log ("consolidating unwind events %i-%i into %i",
+ start_idx, last_idx, start_idx);
+
+ unwind_event *first_event
+ = (unwind_event *)path->get_checker_event (start_idx);
+ const unwind_event *last_event
+ = (const unwind_event *)path->get_checker_event (last_idx);
+ first_event->m_num_frames += last_event->m_num_frames;
+ path->delete_events (start_idx + 1, last_idx - start_idx);
+ }
+}
+
/* Final pass of diagnostic_manager::prune_path.
If all we're left with is in one function, then filter function entry
diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
index 06a8233..b62fc7a 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -229,6 +229,7 @@ private:
void prune_interproc_events (checker_path *path) const;
void prune_system_headers (checker_path *path) const;
void consolidate_conditions (checker_path *path) const;
+ void consolidate_unwind_events (checker_path *path) const;
void finish_pruning (checker_path *path) const;
engine *m_eng;
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 1b819ff..0917306 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1599,6 +1599,24 @@ exploded_node::on_stmt_pre (exploded_graph &eg,
ctxt->maybe_did_work ();
return;
}
+ else if (is_cxa_throw_p (call))
+ {
+ on_throw (eg, call, state, false, ctxt);
+ *out_terminate_path = true;
+ return;
+ }
+ else if (is_cxa_rethrow_p (call))
+ {
+ on_throw (eg, call, state, true, ctxt);
+ *out_terminate_path = true;
+ return;
+ }
+ }
+ else if (const gresx *resx = dyn_cast <const gresx *> (stmt))
+ {
+ on_resx (eg, *resx, state, ctxt);
+ *out_terminate_path = true;
+ return;
}
/* Otherwise, defer to m_region_model. */
@@ -2017,6 +2035,332 @@ exploded_node::on_longjmp (exploded_graph &eg,
}
}
+/* Subclass of call_info for exploded edges that express
+ a throw or rethrow of an exception (actually a call
+ to __cxa_throw or __cxa_rethrow). */
+
+class throw_custom_edge : public call_info
+{
+public:
+ throw_custom_edge (const call_details &cd,
+ tree type,
+ bool is_rethrow)
+ : call_info (cd),
+ m_type (type),
+ m_is_rethrow (is_rethrow)
+ {
+ }
+
+ void print (pretty_printer *pp) const final override
+ {
+ if (m_is_rethrow)
+ {
+ if (m_type)
+ pp_printf (pp, "rethrowing %qT", m_type);
+ else
+ pp_printf (pp, "rethrowing");
+ }
+ else
+ {
+ if (m_type)
+ pp_printf (pp, "throwing %qT", m_type);
+ else
+ pp_printf (pp, "throwing");
+ }
+ }
+
+ void print_desc (pretty_printer &pp) const final override
+ {
+ print (&pp);
+ }
+
+ bool update_model (region_model *model,
+ const exploded_edge *,
+ region_model_context *ctxt) const final override
+ {
+ if (m_is_rethrow)
+ {
+ auto eh_node = model->get_current_caught_exception ();
+ gcc_assert (eh_node);
+ model->push_thrown_exception (*eh_node);
+ }
+ else
+ {
+ call_details cd (get_call_details (model, ctxt));
+
+ const svalue *exception_sval = cd.get_arg_svalue (0);
+ const svalue *tinfo_sval = cd.get_arg_svalue (1);
+ const svalue *destructor_sval = cd.get_arg_svalue (2);
+
+ /* Push a new exception_node on the model's m_exception_stack. */
+ exception_node eh_node (exception_sval, tinfo_sval, destructor_sval);
+ model->push_thrown_exception (eh_node);
+ }
+
+ return true;
+ }
+
+ void add_events_to_path (checker_path *emission_path,
+ const exploded_edge &eedge) const final override
+ {
+ const exploded_node *dst_node = eedge.m_dest;
+ const program_point &dst_point = dst_node->get_point ();
+ const int dst_stack_depth = dst_point.get_stack_depth ();
+
+ const gcall &call = get_call_stmt ();
+
+ emission_path->add_event
+ (std::make_unique<explicit_throw_event>
+ (event_loc_info (call.location,
+ dst_point.get_fndecl (),
+ dst_stack_depth),
+ dst_node,
+ call,
+ m_type,
+ m_is_rethrow));
+ }
+
+private:
+ tree m_type;
+ bool m_is_rethrow;
+};
+
+/* Subclass of custom_edge_info for an exploded edge that expresses
+ unwinding one stack frame during exception handling. */
+
+class unwind_custom_edge : public custom_edge_info
+{
+public:
+ unwind_custom_edge (location_t loc)
+ : m_loc (loc)
+ {
+ }
+
+ void print (pretty_printer *pp) const final override
+ {
+ pp_printf (pp, "unwinding frame");
+ }
+
+ bool update_model (region_model *model,
+ const exploded_edge *,
+ region_model_context *ctxt) const final override
+ {
+ model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false);
+ return true;
+ }
+
+ void add_events_to_path (checker_path *emission_path,
+ const exploded_edge &eedge) const final override
+ {
+ const exploded_node *src_node = eedge.m_src;
+ const program_point &src_point = src_node->get_point ();
+ const int src_stack_depth = src_point.get_stack_depth ();
+ emission_path->add_event
+ (std::make_unique<unwind_event> (event_loc_info (m_loc,
+ src_point.get_fndecl (),
+ src_stack_depth)));
+ }
+
+private:
+ location_t m_loc;
+};
+
+/* Locate an SNODE that's a CFG edge with the EH flag,
+ or return nullptr. */
+
+static const superedge *
+get_eh_outedge (const supernode &snode)
+{
+ for (auto out_sedge : snode.m_succs)
+ if (::edge cfg_edge = out_sedge->get_any_cfg_edge ())
+ if (cfg_edge->flags & EDGE_EH)
+ return out_sedge;
+
+ // Not found
+ return nullptr;
+}
+
+/* Given THROWN_ENODE, which expreses a throw or rethrow occurring at
+ THROW_STMT, unwind intraprocedurally and interprocedurally to find
+ the next eh_dispatch statement to handle exceptions, if any.
+
+ Add eedges and enodes to this graph expressing the actions taken
+ to reach an enode containing the eh_dispatch stmt, if any.
+ Only the final enode is added to this graph's worklist.
+
+ Use CTXT to warn about problems e.g. memory leaks due to stack frames
+ being unwound. */
+
+void
+exploded_graph::unwind_from_exception (exploded_node &thrown_enode,
+ const gimple *throw_stmt,
+ region_model_context *ctxt)
+{
+ logger * const logger = get_logger ();
+ LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index);
+
+ /* Iteratively unwind the stack looking for an out-cfg-edge
+ flagged EH. */
+ exploded_node *iter_enode = &thrown_enode;
+ while (iter_enode)
+ {
+ /* If we have an out-cfg-edge flagged EH, follow that,
+ presumably to a bb with a label and an eh_dispatch stmt.
+ Otherwise assume no out-cfgs-edges, and we are unwinding to the
+ caller. */
+ if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ()))
+ {
+ /* Intraprocedural case.
+ Assume we have an out-edge flagged with EH leading to
+ code for dispatch to catch handlers. */
+ const program_point next_point
+ = program_point::before_supernode (sedge->m_dest,
+ sedge,
+ iter_enode->get_point ().get_call_string ());
+ exploded_node *next_enode
+ = get_or_create_node (next_point,
+ iter_enode->get_state (),
+ iter_enode,
+ /* Add this enode to the worklist. */
+ true);
+ if (!next_enode)
+ return;
+
+ add_edge (iter_enode, next_enode, NULL, false, nullptr);
+ return;
+ }
+ else
+ {
+ /* Interprocedural case.
+ No out-cfg-edge. Unwind one stack frame. */
+ program_state unwound_state (iter_enode->get_state ());
+ location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION;
+ auto unwind_edge_info
+ = std::make_unique<unwind_custom_edge> (loc);
+ unwind_edge_info->update_model (unwound_state.m_region_model, nullptr,
+ ctxt);
+
+ /* Detect leaks in the new state relative to the old state.
+ Use an alternate ctxt that uses the original enode and the stmt
+ (if any) for the location of any diagnostics. */
+ {
+ uncertainty_t uncertainty;
+ impl_region_model_context ctxt (*this,
+ &thrown_enode,
+ &iter_enode->get_state (),
+ &unwound_state,
+ &uncertainty,
+ nullptr,
+ throw_stmt);
+ program_state::detect_leaks (iter_enode->get_state (),
+ unwound_state,
+ NULL,
+ get_ext_state (), &ctxt);
+ }
+ const call_string &cs = iter_enode->get_point ().get_call_string ();
+ if (cs.empty_p ())
+ {
+ /* Top-level stack frame in analysis: unwinding
+ to the outside world that called us. */
+ return;
+ }
+ else
+ {
+ /* Nested function in analysis: unwinding to
+ the callsite in the analysis (or beyond). */
+ program_point unwound_point
+ = program_point::after_supernode (cs.get_caller_node (), cs);
+ unwound_point.pop_from_call_stack ();
+
+ exploded_node *after_unwind_enode
+ = get_or_create_node (unwound_point,
+ std::move (unwound_state),
+ iter_enode,
+ /* Don't add this enode to the
+ worklist; we will process it
+ on the next iteration. */
+ false);
+
+ if (!after_unwind_enode)
+ return;
+
+ add_edge (iter_enode, after_unwind_enode, NULL, true,
+ std::move (unwind_edge_info));
+ iter_enode = after_unwind_enode;
+ }
+ }
+ }
+}
+
+/* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow.
+
+ Create an eedge and destination enode for the throw/rethrow, adding
+ them to this egraph. The new enode isn't added to the worklist, but
+ instead exploded_graph::unwind_from_exception is immediately called
+ on it, potentially creating more eedges and enodes leading to an
+ eh_handler stmt. */
+
+void
+exploded_node::on_throw (exploded_graph &eg,
+ const gcall &throw_call,
+ program_state *new_state,
+ bool is_rethrow,
+ region_model_context *ctxt)
+{
+ region_model *model = new_state->m_region_model;
+ call_details cd (throw_call, model, ctxt);
+
+ /* Create an enode and eedge for the "throw". */
+ tree type = NULL_TREE;
+ if (is_rethrow)
+ {
+ const exception_node *eh_node = model->get_current_caught_exception ();
+ gcc_assert (eh_node);
+ type = eh_node->maybe_get_type ();
+ }
+ else
+ {
+ const svalue *tinfo_sval = cd.get_arg_svalue (1);
+ type = tinfo_sval->maybe_get_type_from_typeinfo ();
+ }
+ auto throw_edge_info
+ = std::make_unique<throw_custom_edge> (cd, type, is_rethrow);
+ throw_edge_info->update_model (model, nullptr, ctxt);
+
+ program_point after_throw_point = get_point ().get_next ();
+
+ exploded_node *after_throw_enode
+ = eg.get_or_create_node (after_throw_point, *new_state, this,
+ /* Don't add to worklist; we process
+ this immediately below. */
+ false);
+
+ if (!after_throw_enode)
+ return;
+
+ /* Create custom exploded_edge for a throw. */
+ eg.add_edge (this, after_throw_enode, NULL, true,
+ std::move (throw_edge_info));
+
+ eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt);
+}
+
+/* Handle a gimple "resx" statement by adding eedges and enode.
+ that unwind to the next eh_dispatch statement, if any. Only
+ the final enode is added to the worklist. */
+
+void
+exploded_node::on_resx (exploded_graph &eg,
+ const gresx &/*resx*/,
+ program_state */*new_state*/,
+ region_model_context *ctxt)
+{
+ eg.unwind_from_exception (*this,
+ nullptr,
+ ctxt);
+}
+
+
/* Subroutine of exploded_graph::process_node for finding the successors
of the supernode for a function exit basic block.
@@ -2839,7 +3183,8 @@ exploded_graph::add_function_entry (const function &fun)
}
/* Get or create an exploded_node for (POINT, STATE).
- If a new node is created, it is added to the worklist.
+ If a new node is created and ADD_TO_WORKLIST is true,
+ it is added to the worklist.
Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
that need to be emitted (e.g. when purging state *before* we have
@@ -2848,7 +3193,8 @@ exploded_graph::add_function_entry (const function &fun)
exploded_node *
exploded_graph::get_or_create_node (const program_point &point,
const program_state &state,
- exploded_node *enode_for_diag)
+ exploded_node *enode_for_diag,
+ bool add_to_worklist)
{
logger * const logger = get_logger ();
LOG_FUNC (logger);
@@ -3023,7 +3369,10 @@ exploded_graph::get_or_create_node (const program_point &point,
}
/* Add the new node to the worlist. */
- m_worklist.add_node (node);
+ if (add_to_worklist)
+ m_worklist.add_node (node);
+ else
+ node->set_status (exploded_node::status::special);
return node;
}
@@ -4258,12 +4607,18 @@ exploded_graph::process_node (exploded_node *node)
NULL, /* no exploded_edge yet. */
&bifurcation_ctxt))
{
- exploded_node *next2
- = get_or_create_node (next_point, bifurcated_new_state, node);
- if (next2)
- add_edge (node, next2, NULL,
- true /* assume that work could be done */,
- std::move (edge_info));
+ if (exploded_node *next2
+ = edge_info->create_enode
+ (*this,
+ next_point,
+ std::move (bifurcated_new_state),
+ node,
+ &bifurcation_ctxt))
+ {
+ add_edge (node, next2, NULL,
+ true /* assume that work could be done */,
+ std::move (edge_info));
+ }
}
else
{
@@ -4384,6 +4739,18 @@ exploded_graph::process_node (exploded_node *node)
}
}
+ /* Ignore CFG edges in the sgraph flagged with EH whilst
+ we're exploring the egraph.
+ We only use these sedges in special-case logic for
+ dealing with exception-handling. */
+ if (auto cfg_sedge = succ->dyn_cast_cfg_superedge ())
+ if (cfg_sedge->get_flags () & EDGE_EH)
+ {
+ if (logger)
+ logger->log ("rejecting EH edge");
+ continue;
+ }
+
if (!node->on_edge (*this, succ, &next_point, &next_state,
&uncertainty))
{
@@ -6002,6 +6369,9 @@ private:
break;
case exploded_node::status::processed:
break;
+ case exploded_node::status::special:
+ pp_string (pp, "(S)");
+ break;
case exploded_node::status::merger:
pp_string (pp, "(M)");
break;
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index aa6fb1f..32c72dc 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -214,6 +214,10 @@ class exploded_node : public dnode<eg_traits>
/* Node has had exploded_graph::process_node called on it. */
processed,
+ /* Node was excluded from worklist on creation.
+ e.g. for handling exception-unwinding. */
+ special,
+
/* Node was left unprocessed due to merger; it won't have had
exploded_graph::process_node called on it. */
merger,
@@ -306,6 +310,15 @@ class exploded_node : public dnode<eg_traits>
const gcall &call,
program_state *new_state,
region_model_context *ctxt);
+ void on_throw (exploded_graph &eg,
+ const gcall &call,
+ program_state *new_state,
+ bool is_rethrow,
+ region_model_context *ctxt);
+ void on_resx (exploded_graph &eg,
+ const gresx &resx,
+ program_state *new_state,
+ region_model_context *ctxt);
void detect_leaks (exploded_graph &eg);
@@ -827,7 +840,8 @@ public:
exploded_node *get_or_create_node (const program_point &point,
const program_state &state,
- exploded_node *enode_for_diag);
+ exploded_node *enode_for_diag,
+ bool add_to_worklist = true);
exploded_edge *add_edge (exploded_node *src, exploded_node *dest,
const superedge *sedge, bool could_do_work,
std::unique_ptr<custom_edge_info> custom = NULL);
@@ -881,6 +895,10 @@ public:
void on_escaped_function (tree fndecl);
+ void unwind_from_exception (exploded_node &enode,
+ const gimple *stmt,
+ region_model_context *ctxt);
+
/* In infinite-loop.cc */
void detect_infinite_loops ();
diff --git a/gcc/analyzer/kf-lang-cp.cc b/gcc/analyzer/kf-lang-cp.cc
index 8e08dbc..01a98b0 100644
--- a/gcc/analyzer/kf-lang-cp.cc
+++ b/gcc/analyzer/kf-lang-cp.cc
@@ -167,6 +167,161 @@ public:
};
+class kf_cxa_allocate_exception : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return cd.num_args () == 1 && cd.arg_is_size_p (0);
+ }
+
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ region_model *model = cd.get_model ();
+ region_model_manager *mgr = cd.get_manager ();
+ const svalue *size_sval = cd.get_arg_svalue (0);
+ region_model_context *ctxt = cd.get_ctxt ();
+
+ /* Create a heap allocated region. */
+ const region *new_reg
+ = model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
+ if (cd.get_lhs_type ())
+ {
+ const svalue *ptr_sval
+ = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+ cd.maybe_set_lhs (ptr_sval);
+ }
+ }
+};
+
+class kf_cxa_begin_catch : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 1
+ && POINTER_TYPE_P (cd.get_arg_type (0)));
+ }
+
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ region_model *model = cd.get_model ();
+
+ auto node = model->pop_thrown_exception ();
+ model->push_caught_exception (node);
+ cd.maybe_set_lhs (node.m_exception_sval);
+ }
+};
+
+class kf_cxa_end_catch : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return cd.num_args () == 0;
+ }
+
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ region_model *model = cd.get_model ();
+ model->pop_caught_exception ();
+ }
+};
+
+/* A subclass of pending_diagnostic for complaining about an exception
+ of an unexpected type being thrown (due to a call to
+ __cxa_call_unexpected).
+ See https://en.cppreference.com/w/cpp/language/except_spec */
+
+class throw_of_unexpected_type
+: public pending_diagnostic_subclass<throw_of_unexpected_type>
+{
+public:
+ throw_of_unexpected_type (tree exception_type,
+ tree thrown_from_fndecl)
+ : m_exception_type (exception_type),
+ m_thrown_from_fndecl (thrown_from_fndecl)
+ {
+ gcc_assert (m_exception_type);
+ gcc_assert (m_thrown_from_fndecl);
+ }
+
+ const char *get_kind () const final override
+ {
+ return "throw_of_unexpected_type";
+ }
+
+ bool operator== (const throw_of_unexpected_type &other) const
+ {
+ return (m_exception_type == other.m_exception_type
+ && m_thrown_from_fndecl == other.m_thrown_from_fndecl);
+ }
+
+ int get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_throw_of_unexpected_type;
+ }
+
+ bool emit (diagnostic_emission_context &ctxt) final override
+ {
+ auto_diagnostic_group d;
+
+ bool warned
+ = ctxt.warn ("throwing exception of unexpected type %qT from %qE",
+ m_exception_type, m_thrown_from_fndecl);
+ if (warned)
+ {
+ inform (DECL_SOURCE_LOCATION (m_thrown_from_fndecl),
+ "%qE declared here", m_thrown_from_fndecl);
+ // TODO: show specified types?
+ }
+ return warned;
+ }
+
+ bool
+ describe_final_event (pretty_printer &pp,
+ const evdesc::final_event &) final override
+ {
+ pp_printf (&pp,
+ "exception of unexpected type %qT thrown from %qE",
+ m_exception_type, m_thrown_from_fndecl);
+ return true;
+ }
+
+private:
+ tree m_exception_type;
+ tree m_thrown_from_fndecl;
+};
+
+/* See https://en.cppreference.com/w/cpp/language/except_spec */
+
+class kf_cxa_call_unexpected : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 1
+ && POINTER_TYPE_P (cd.get_arg_type (0)));
+ }
+
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ if (region_model_context *ctxt = cd.get_ctxt ())
+ {
+ region_model *model = cd.get_model ();
+ tree thrown_from_fndecl = model->get_current_function ()->decl;
+ /* We must have a thrown exception. */
+ auto eh_node = model->get_current_thrown_exception ();
+ gcc_assert (eh_node);
+ tree exception_type = eh_node->maybe_get_type ();
+ ctxt->warn
+ (std::make_unique<throw_of_unexpected_type> (exception_type,
+ thrown_from_fndecl));
+ ctxt->terminate_path ();
+ }
+ }
+};
+
/* Populate KFM with instances of known functions relating to C++. */
void
@@ -176,6 +331,17 @@ register_known_functions_lang_cp (known_function_manager &kfm)
kfm.add ("operator new []", std::make_unique<kf_operator_new> ());
kfm.add ("operator delete", std::make_unique<kf_operator_delete> ());
kfm.add ("operator delete []", std::make_unique<kf_operator_delete> ());
+
+ /* Functions mentioned in "Itanium C++ ABI: Exception Handling"'s
+ "Level II: C++ ABI"
+ https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi */
+ kfm.add ("__cxa_allocate_exception",
+ std::make_unique<kf_cxa_allocate_exception> ());
+ // We treat __cxa_throw and __cxa_rethrow as special cases
+ kfm.add ("__cxa_begin_catch", std::make_unique<kf_cxa_begin_catch> ());
+ kfm.add ("__cxa_end_catch", std::make_unique<kf_cxa_end_catch> ());
+ kfm.add ("__cxa_call_unexpected",
+ std::make_unique<kf_cxa_call_unexpected> ());
}
} // namespace ana
diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc
index e9ad865..75b6279 100644
--- a/gcc/analyzer/kf.cc
+++ b/gcc/analyzer/kf.cc
@@ -1276,6 +1276,27 @@ public:
/* Currently a no-op. */
};
+/* Handler for "__builtin_eh_pointer". */
+
+class kf_eh_pointer : public builtin_known_function
+{
+public:
+ bool matches_call_types_p (const call_details &) const final override
+ {
+ return true;
+ }
+
+ enum built_in_function builtin_code () const final override
+ {
+ return BUILT_IN_EH_POINTER;
+ }
+
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ cd.set_any_lhs_with_defaults ();
+ }
+};
+
/* Handler for "strcat" and "__builtin_strcat_chk". */
class kf_strcat : public builtin_known_function
@@ -2250,6 +2271,8 @@ register_known_functions (known_function_manager &kfm,
kfm.add (BUILT_IN_STACK_RESTORE, std::make_unique<kf_stack_restore> ());
kfm.add (BUILT_IN_STACK_SAVE, std::make_unique<kf_stack_save> ());
+ kfm.add (BUILT_IN_EH_POINTER, std::make_unique<kf_eh_pointer> ());
+
register_atomic_builtins (kfm);
register_sanitizer_builtins (kfm);
register_varargs_builtins (kfm);
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index ea0e6b0..fc58629 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/checker-path.h"
#include "analyzer/feasible-graph.h"
#include "analyzer/record-layout.h"
+#include "analyzer/function-set.h"
#if ENABLE_ANALYZER
@@ -314,12 +315,97 @@ region_to_value_map::purge_state_involving (const svalue *sval)
m_hash_map.remove (iter);
}
+// struct exception_node
+
+bool
+exception_node::operator== (const exception_node &other) const
+{
+ return (m_exception_sval == other.m_exception_sval
+ && m_typeinfo_sval == other.m_typeinfo_sval
+ && m_destructor_sval == other.m_destructor_sval);
+}
+
+void
+exception_node::dump_to_pp (pretty_printer *pp,
+ bool simple) const
+{
+ pp_printf (pp, "{exception: ");
+ m_exception_sval->dump_to_pp (pp, simple);
+ pp_string (pp, ", typeinfo: ");
+ m_typeinfo_sval->dump_to_pp (pp, simple);
+ pp_string (pp, ", destructor: ");
+ m_destructor_sval->dump_to_pp (pp, simple);
+ pp_string (pp, "}");
+}
+
+void
+exception_node::dump (FILE *fp, bool simple) const
+{
+ tree_dump_pretty_printer pp (fp);
+ dump_to_pp (&pp, simple);
+ pp_newline (&pp);
+}
+
+/* Dump a multiline representation of this model to stderr. */
+
+DEBUG_FUNCTION void
+exception_node::dump (bool simple) const
+{
+ dump (stderr, simple);
+}
+
+DEBUG_FUNCTION void
+exception_node::dump () const
+{
+ text_art::dump (*this);
+}
+
+std::unique_ptr<json::object>
+exception_node::to_json () const
+{
+ auto obj = std::make_unique<json::object> ();
+ obj->set ("exception", m_exception_sval->to_json ());
+ obj->set ("typeinfo", m_typeinfo_sval->to_json ());
+ obj->set ("destructor", m_destructor_sval->to_json ());
+ return obj;
+}
+
+std::unique_ptr<text_art::tree_widget>
+exception_node::make_dump_widget (const text_art::dump_widget_info &dwi) const
+{
+ using text_art::tree_widget;
+ std::unique_ptr<tree_widget> w
+ (tree_widget::from_fmt (dwi, nullptr, "Exception Node"));
+
+ w->add_child (m_exception_sval->make_dump_widget (dwi, "exception"));
+ w->add_child (m_typeinfo_sval->make_dump_widget (dwi, "typeinfo"));
+ w->add_child (m_destructor_sval->make_dump_widget (dwi, "destructor"));
+
+ return w;
+}
+
+tree
+exception_node::maybe_get_type () const
+{
+ return m_typeinfo_sval->maybe_get_type_from_typeinfo ();
+}
+
+void
+exception_node::add_to_reachable_regions (reachable_regions &regs) const
+{
+ regs.handle_sval (m_exception_sval);
+ regs.handle_sval (m_typeinfo_sval);
+ regs.handle_sval (m_destructor_sval);
+}
+
/* class region_model. */
/* Ctor for region_model: construct an "empty" model. */
region_model::region_model (region_model_manager *mgr)
: m_mgr (mgr), m_store (), m_current_frame (NULL),
+ m_thrown_exceptions_stack (),
+ m_caught_exceptions_stack (),
m_dynamic_extents ()
{
m_constraints = new constraint_manager (mgr);
@@ -331,6 +417,8 @@ region_model::region_model (const region_model &other)
: m_mgr (other.m_mgr), m_store (other.m_store),
m_constraints (new constraint_manager (*other.m_constraints)),
m_current_frame (other.m_current_frame),
+ m_thrown_exceptions_stack (other.m_thrown_exceptions_stack),
+ m_caught_exceptions_stack (other.m_caught_exceptions_stack),
m_dynamic_extents (other.m_dynamic_extents)
{
}
@@ -357,6 +445,9 @@ region_model::operator= (const region_model &other)
m_current_frame = other.m_current_frame;
+ m_thrown_exceptions_stack = other.m_thrown_exceptions_stack;
+ m_caught_exceptions_stack = other.m_caught_exceptions_stack;
+
m_dynamic_extents = other.m_dynamic_extents;
return *this;
@@ -383,6 +474,11 @@ region_model::operator== (const region_model &other) const
if (m_current_frame != other.m_current_frame)
return false;
+ if (m_thrown_exceptions_stack != other.m_thrown_exceptions_stack)
+ return false;
+ if (m_caught_exceptions_stack != other.m_caught_exceptions_stack)
+ return false;
+
if (m_dynamic_extents != other.m_dynamic_extents)
return false;
@@ -409,7 +505,7 @@ void
region_model::dump_to_pp (pretty_printer *pp, bool simple,
bool multiline) const
{
- /* Dump stack. */
+ /* Dump frame stack. */
pp_printf (pp, "stack depth: %i", get_stack_depth ());
if (multiline)
pp_newline (pp);
@@ -430,6 +526,50 @@ region_model::dump_to_pp (pretty_printer *pp, bool simple,
if (!multiline)
pp_string (pp, "}");
+ /* Dump exception stacks. */
+ if (m_thrown_exceptions_stack.size () > 0)
+ {
+ pp_printf (pp, "thrown exceptions: %i", (int)m_thrown_exceptions_stack.size ());
+ if (multiline)
+ pp_newline (pp);
+ else
+ pp_string (pp, " {");
+ for (size_t idx = 0; idx < m_thrown_exceptions_stack.size (); ++idx)
+ {
+ if (multiline)
+ pp_string (pp, " ");
+ else if (idx > 0)
+ pp_string (pp, ", ");
+ pp_printf (pp, "exception (index %i): ", (int)idx);
+ m_thrown_exceptions_stack[idx].dump_to_pp (pp, simple);
+ if (multiline)
+ pp_newline (pp);
+ }
+ if (!multiline)
+ pp_string (pp, "}");
+ }
+ if (m_caught_exceptions_stack.size () > 0)
+ {
+ pp_printf (pp, "caught exceptions: %i", (int)m_caught_exceptions_stack.size ());
+ if (multiline)
+ pp_newline (pp);
+ else
+ pp_string (pp, " {");
+ for (size_t idx = 0; idx < m_caught_exceptions_stack.size (); ++idx)
+ {
+ if (multiline)
+ pp_string (pp, " ");
+ else if (idx > 0)
+ pp_string (pp, ", ");
+ pp_printf (pp, "exception (index %i): ", (int)idx);
+ m_caught_exceptions_stack[idx].dump_to_pp (pp, simple);
+ if (multiline)
+ pp_newline (pp);
+ }
+ if (!multiline)
+ pp_string (pp, "}");
+ }
+
/* Dump store. */
if (!multiline)
pp_string (pp, ", {");
@@ -502,6 +642,17 @@ region_model::to_json () const
model_obj->set ("constraints", m_constraints->to_json ());
if (m_current_frame)
model_obj->set ("current_frame", m_current_frame->to_json ());
+
+ auto thrown_exceptions_arr = std::make_unique<json::array> ();
+ for (auto &node : m_thrown_exceptions_stack)
+ thrown_exceptions_arr->append (node.to_json ());
+ model_obj->set ("thrown_exception_stack", std::move (thrown_exceptions_arr));
+
+ auto caught_exceptions_arr = std::make_unique<json::array> ();
+ for (auto &node : m_caught_exceptions_stack)
+ caught_exceptions_arr->append (node.to_json ());
+ model_obj->set ("caught_exception_stack", std::move (caught_exceptions_arr));
+
model_obj->set ("dynamic_extents", m_dynamic_extents.to_json ());
return model_obj;
}
@@ -525,6 +676,26 @@ region_model::make_dump_widget (const text_art::dump_widget_info &dwi) const
m_current_frame->dump_to_pp (pp, simple);
model_widget->add_child (tree_widget::make (dwi, pp));
}
+
+ if (m_thrown_exceptions_stack.size () > 0)
+ {
+ auto thrown_exceptions_widget
+ = tree_widget::make (dwi, "Thrown Exceptions");
+ for (auto &thrown_exception : m_thrown_exceptions_stack)
+ thrown_exceptions_widget->add_child
+ (thrown_exception.make_dump_widget (dwi));
+ model_widget->add_child (std::move (thrown_exceptions_widget));
+ }
+ if (m_caught_exceptions_stack.size () > 0)
+ {
+ auto caught_exceptions_widget
+ = tree_widget::make (dwi, "Caught Exceptions");
+ for (auto &caught_exception : m_caught_exceptions_stack)
+ caught_exceptions_widget->add_child
+ (caught_exception.make_dump_widget (dwi));
+ model_widget->add_child (std::move (caught_exceptions_widget));
+ }
+
model_widget->add_child
(m_store.make_dump_widget (dwi,
m_mgr->get_store_manager ()));
@@ -1906,6 +2077,170 @@ region_model::get_builtin_kf (const gcall &call,
return NULL;
}
+/* Subclass of custom_edge_info for use by exploded_edges that represent
+ an exception being thrown from a call we don't have the code for. */
+
+class exception_thrown_from_unrecognized_call : public custom_edge_info
+{
+public:
+ exception_thrown_from_unrecognized_call (const gcall &call,
+ tree fndecl)
+ : m_call (call),
+ m_fndecl (fndecl)
+ {
+ }
+
+ void print (pretty_printer *pp) const
+ {
+ if (m_fndecl)
+ pp_printf (pp, "if %qD throws an exception...", m_fndecl);
+ else
+ pp_printf (pp, "if the called function throws an exception...");
+ };
+
+ bool
+ update_model (region_model *model,
+ const exploded_edge *,
+ region_model_context *ctxt) const final override
+ {
+ /* Allocate an exception and set it as the current exception. */
+ const region *exception_reg
+ = model->get_or_create_region_for_heap_alloc
+ (nullptr, /* We don't know the size of the region. */
+ ctxt);
+
+ region_model_manager *mgr = model->get_manager ();
+ conjured_purge p (model, ctxt);
+
+ /* The contents of the region are some conjured svalue. */
+ const svalue *exception_sval
+ = mgr->get_or_create_conjured_svalue (NULL_TREE,
+ &m_call,
+ exception_reg, p, 0);
+ model->set_value (exception_reg, exception_sval, ctxt);
+ const svalue *exception_ptr_sval
+ = mgr->get_ptr_svalue (ptr_type_node, exception_reg);
+ const svalue *tinfo_sval
+ = mgr->get_or_create_conjured_svalue (ptr_type_node,
+ &m_call,
+ exception_reg, p, 1);
+ const svalue *destructor_sval
+ = mgr->get_or_create_conjured_svalue (ptr_type_node,
+ &m_call,
+ exception_reg, p, 2);
+
+ /* Push a new exception_node on the model's thrown exception stack. */
+ exception_node eh_node (exception_ptr_sval, tinfo_sval, destructor_sval);
+ model->push_thrown_exception (eh_node);
+
+ return true;
+ }
+
+ void
+ add_events_to_path (checker_path *emission_path,
+ const exploded_edge &eedge) const final override
+ {
+ const exploded_node *dst_node = eedge.m_dest;
+ const program_point &dst_point = dst_node->get_point ();
+ const int dst_stack_depth = dst_point.get_stack_depth ();
+
+ emission_path->add_event
+ (std::make_unique<throw_from_call_to_external_fn_event>
+ (event_loc_info (m_call.location,
+ dst_point.get_fndecl (),
+ dst_stack_depth),
+ dst_node,
+ m_call,
+ m_fndecl));
+ }
+
+ exploded_node *
+ create_enode (exploded_graph &eg,
+ const program_point &point,
+ program_state &&state,
+ exploded_node *enode_for_diag,
+ region_model_context *ctxt) const final override
+ {
+ exploded_node *thrown_enode
+ = eg.get_or_create_node (point, state, enode_for_diag,
+ /* Don't add to worklist. */
+ false);
+ if (!thrown_enode)
+ return nullptr;
+
+ /* Add successor edges for thrown_enode "by hand" for the exception. */
+ eg.unwind_from_exception (*thrown_enode,
+ &m_call,
+ ctxt);
+ return thrown_enode;
+ }
+
+private:
+ const gcall &m_call;
+ tree m_fndecl; // could be null
+};
+
+/* Get a set of functions that are assumed to not throw exceptions. */
+
+static function_set
+get_fns_assumed_not_to_throw ()
+{
+ // TODO: populate this list more fully
+ static const char * const fn_names[] = {
+ /* This array must be kept sorted. */
+
+ "fclose"
+ };
+ const size_t count = ARRAY_SIZE (fn_names);
+ function_set fs (fn_names, count);
+ return fs;
+}
+
+/* Return true if CALL could throw an exception.
+ FNDECL could be NULL_TREE. */
+
+static bool
+can_throw_p (const gcall &call, tree fndecl)
+{
+ if (!flag_exceptions)
+ return false;
+
+ if (gimple_call_nothrow_p (&call))
+ return false;
+
+ if (fndecl)
+ {
+ const function_set fs = get_fns_assumed_not_to_throw ();
+ if (fs.contains_decl_p (fndecl))
+ return false;
+ }
+
+ return true;
+}
+
+/* Given CALL where we don't know what code is being called
+ (by not having the body of FNDECL, or having NULL_TREE for FNDECL),
+ potentially bifurcate control flow to simulate the call throwing
+ an exception. */
+
+void
+region_model::check_for_throw_inside_call (const gcall &call,
+ tree fndecl,
+ region_model_context *ctxt)
+{
+ if (!ctxt)
+ return;
+
+ /* Could this function throw an exception?
+ If so, add an extra e-edge for that. */
+ if (!can_throw_p (call, fndecl))
+ return;
+
+ auto throws_exception
+ = std::make_unique<exception_thrown_from_unrecognized_call> (call, fndecl);
+ ctxt->bifurcate (std::move (throws_exception));
+}
+
/* Update this model for the CALL stmt, using CTXT to report any
diagnostics - the first half.
@@ -1951,6 +2286,7 @@ region_model::on_call_pre (const gcall &call, region_model_context *ctxt)
if (!callee_fndecl)
{
+ check_for_throw_inside_call (call, NULL_TREE, ctxt);
cd.set_any_lhs_with_defaults ();
return true; /* Unknown side effects. */
}
@@ -1971,7 +2307,10 @@ region_model::on_call_pre (const gcall &call, region_model_context *ctxt)
return true; /* Unknown side effects. */
if (!fndecl_has_gimple_body_p (callee_fndecl))
- return true; /* Unknown side effects. */
+ {
+ check_for_throw_inside_call (call, callee_fndecl, ctxt);
+ return true; /* Unknown side effects. */
+ }
return false; /* No side effects. */
}
@@ -5884,17 +6223,22 @@ region_model::maybe_update_for_edge (const superedge &edge,
ctxt, out);
}
+ if (const geh_dispatch *eh_dispatch_stmt
+ = dyn_cast <const geh_dispatch *> (last_stmt))
+ {
+ const eh_dispatch_cfg_superedge *eh_dispatch_cfg_sedge
+ = as_a <const eh_dispatch_cfg_superedge *> (&edge);
+ return apply_constraints_for_eh_dispatch (*eh_dispatch_cfg_sedge,
+ eh_dispatch_stmt,
+ ctxt, out);
+ }
+
if (const ggoto *goto_stmt = dyn_cast <const ggoto *> (last_stmt))
{
const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (&edge);
return apply_constraints_for_ggoto (*cfg_sedge, goto_stmt, ctxt);
}
- /* Apply any constraints due to an exception being thrown. */
- if (const cfg_superedge *cfg_sedge = dyn_cast <const cfg_superedge *> (&edge))
- if (cfg_sedge->get_flags () & EDGE_EH)
- return apply_constraints_for_exception (last_stmt, ctxt, out);
-
return true;
}
@@ -6171,6 +6515,173 @@ apply_constraints_for_gswitch (const switch_cfg_superedge &edge,
return sat;
}
+class rejected_eh_dispatch : public rejected_constraint
+{
+public:
+ rejected_eh_dispatch (const region_model &model)
+ : rejected_constraint (model)
+ {}
+
+ void dump_to_pp (pretty_printer *pp) const final override
+ {
+ pp_printf (pp, "rejected_eh_dispatch");
+ }
+};
+
+static bool
+exception_matches_type_p (tree exception_type,
+ tree catch_type)
+{
+ if (catch_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;
+}
+
+static bool
+matches_any_exception_type_p (eh_catch ehc, tree exception_type)
+{
+ if (ehc->type_list == NULL_TREE)
+ /* All exceptions are caught here. */
+ return true;
+
+ for (tree iter = ehc->type_list; iter; iter = TREE_CHAIN (iter))
+ if (exception_matches_type_p (TREE_VALUE (iter),
+ exception_type))
+ return true;
+ return false;
+}
+
+bool
+region_model::
+apply_constraints_for_eh_dispatch (const eh_dispatch_cfg_superedge &edge,
+ const geh_dispatch *,
+ region_model_context *ctxt,
+ std::unique_ptr<rejected_constraint> *out)
+{
+ const exception_node *current_node = get_current_thrown_exception ();
+ gcc_assert (current_node);
+ tree curr_exception_type = current_node->maybe_get_type ();
+ if (!curr_exception_type)
+ /* We don't know the specific type. */
+ return true;
+
+ return edge.apply_constraints (this, ctxt, curr_exception_type, out);
+}
+
+bool
+region_model::
+apply_constraints_for_eh_dispatch_try (const eh_dispatch_try_cfg_superedge &edge,
+ region_model_context */*ctxt*/,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out)
+{
+ /* TODO: can we rely on this ordering?
+ or do we need to iterate through prev_catch ? */
+ /* The exception must not match any of the previous edges. */
+ for (auto sibling_sedge : edge.m_src->m_succs)
+ {
+ if (sibling_sedge == &edge)
+ break;
+
+ const eh_dispatch_try_cfg_superedge *sibling_eh_sedge
+ = as_a <const eh_dispatch_try_cfg_superedge *> (sibling_sedge);
+ if (eh_catch ehc = sibling_eh_sedge->get_eh_catch ())
+ if (matches_any_exception_type_p (ehc, exception_type))
+ {
+ /* The earlier sibling matches, so the "unhandled" edge is
+ not taken. */
+ if (out)
+ *out = std::make_unique<rejected_eh_dispatch> (*this);
+ return false;
+ }
+ }
+
+ if (eh_catch ehc = edge.get_eh_catch ())
+ {
+ /* We have an edge that tried to match one or more types. */
+
+ /* The exception must not match any of the previous edges. */
+
+ /* It must match this type. */
+ if (matches_any_exception_type_p (ehc, exception_type))
+ return true;
+ else
+ {
+ /* Exception type doesn't match. */
+ if (out)
+ *out = std::make_unique<rejected_eh_dispatch> (*this);
+ return false;
+ }
+ }
+ else
+ {
+ /* This is the "unhandled exception" edge.
+ If we get here then no sibling edges matched;
+ we will follow this edge. */
+ return true;
+ }
+}
+
+bool
+region_model::
+apply_constraints_for_eh_dispatch_allowed (const eh_dispatch_allowed_cfg_superedge &edge,
+ region_model_context */*ctxt*/,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out)
+{
+ auto curr_thrown_exception_node = get_current_thrown_exception ();
+ gcc_assert (curr_thrown_exception_node);
+ tree curr_exception_type = curr_thrown_exception_node->maybe_get_type ();
+ eh_region eh_reg = edge.get_eh_region ();
+ tree type_list = eh_reg->u.allowed.type_list;
+
+ switch (edge.get_eh_kind ())
+ {
+ default:
+ gcc_unreachable ();
+ case eh_dispatch_allowed_cfg_superedge::eh_kind::expected:
+ if (!curr_exception_type)
+ {
+ /* We don't know the specific type;
+ assume we have one of an expected type. */
+ return true;
+ }
+ for (tree iter = type_list; iter; iter = TREE_CHAIN (iter))
+ if (exception_matches_type_p (TREE_VALUE (iter),
+ exception_type))
+ return true;
+ if (out)
+ *out = std::make_unique<rejected_eh_dispatch> (*this);
+ return false;
+
+ case eh_dispatch_allowed_cfg_superedge::eh_kind::unexpected:
+ if (!curr_exception_type)
+ {
+ /* We don't know the specific type;
+ assume we don't have one of an expected type. */
+ if (out)
+ *out = std::make_unique<rejected_eh_dispatch> (*this);
+ return false;
+ }
+ for (tree iter = type_list; iter; iter = TREE_CHAIN (iter))
+ if (exception_matches_type_p (TREE_VALUE (iter),
+ exception_type))
+ {
+ if (out)
+ *out = std::make_unique<rejected_eh_dispatch> (*this);
+ return false;
+ }
+ return true;
+ }
+}
+
/* Given an edge reached by GOTO_STMT, determine appropriate constraints
for the edge to be taken.
@@ -6202,38 +6713,6 @@ region_model::apply_constraints_for_ggoto (const cfg_superedge &edge,
return true;
}
-/* Apply any constraints due to an exception being thrown at LAST_STMT.
-
- If they are feasible, add the constraints and return true.
-
- Return false if the constraints contradict existing knowledge
- (and so the edge should not be taken).
- When returning false, if OUT is non-NULL, write a new rejected_constraint
- to it. */
-
-bool
-region_model::
-apply_constraints_for_exception (const gimple *last_stmt,
- region_model_context *ctxt,
- std::unique_ptr<rejected_constraint> *out)
-{
- gcc_assert (last_stmt);
- if (const gcall *call = dyn_cast <const gcall *> (last_stmt))
- if (tree callee_fndecl = get_fndecl_for_call (*call, ctxt))
- if (is_named_call_p (callee_fndecl, "operator new", *call, 1)
- || is_named_call_p (callee_fndecl, "operator new []", *call, 1))
- {
- /* We have an exception thrown from operator new.
- Add a constraint that the result was NULL, to avoid a false
- leak report due to the result being lost when following
- the EH edge. */
- if (tree lhs = gimple_call_lhs (call))
- return add_constraint (lhs, EQ_EXPR, null_pointer_node, ctxt, out);
- return true;
- }
- return true;
-}
-
/* For use with push_frame when handling a top-level call within the analysis.
PARAM has a defined but unknown initial value.
Anything it points to has escaped, since the calling context "knows"
@@ -6640,6 +7119,14 @@ region_model::can_merge_with_p (const region_model &other_model,
for (auto iter : m.m_svals_changing_meaning)
out_model->m_constraints->purge_state_involving (iter);
+ if (m_thrown_exceptions_stack != other_model.m_thrown_exceptions_stack)
+ return false;
+ out_model->m_thrown_exceptions_stack = m_thrown_exceptions_stack;
+
+ if (m_caught_exceptions_stack != other_model.m_caught_exceptions_stack)
+ return false;
+ out_model->m_caught_exceptions_stack = m_caught_exceptions_stack;
+
return true;
}
@@ -6894,6 +7381,12 @@ region_model::get_referenced_base_regions (auto_bitmap &out_ids) const
reachable_regs.add (base_reg, false);
}
+ for (auto &eh_node : m_thrown_exceptions_stack)
+ eh_node.add_to_reachable_regions (reachable_regs);
+ for (auto &eh_node : m_caught_exceptions_stack)
+ eh_node.add_to_reachable_regions (reachable_regs);
+
+
bitmap_clear (out_ids);
for (auto iter_reg : reachable_regs)
bitmap_set_bit (out_ids, iter_reg->get_id ());
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 3999473..c1eb946 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -250,6 +250,41 @@ typedef void (*pop_frame_callback) (const region_model *model,
const svalue *retval,
region_model_context *ctxt);
+/* Roughly equivalent to a struct __cxa_exception, except we store a std::vector
+ rather than a linked list. */
+
+struct exception_node
+{
+ exception_node (const svalue *exception_sval,
+ const svalue *typeinfo_sval,
+ const svalue *destructor_sval)
+ : m_exception_sval (exception_sval),
+ m_typeinfo_sval (typeinfo_sval),
+ m_destructor_sval (destructor_sval)
+ {
+ }
+
+ bool operator== (const exception_node &other) const;
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const;
+ void dump (FILE *fp, bool simple) const;
+ void dump (bool simple) const;
+ void dump () const;
+
+ std::unique_ptr<json::object> to_json () const;
+
+ std::unique_ptr<text_art::tree_widget>
+ make_dump_widget (const text_art::dump_widget_info &dwi) const;
+
+ tree maybe_get_type () const;
+
+ void add_to_reachable_regions (reachable_regions &) const;
+
+ const svalue *m_exception_sval;
+ const svalue *m_typeinfo_sval;
+ const svalue *m_destructor_sval;
+};
+
/* A region_model encapsulates a representation of the state of memory, with
a tree of regions, along with their associated values.
The representation is graph-like because values can be pointers to
@@ -583,6 +618,56 @@ class region_model
bool called_from_main_p () const;
+ void push_thrown_exception (const exception_node &node)
+ {
+ m_thrown_exceptions_stack.push_back (node);
+ }
+ const exception_node *get_current_thrown_exception () const
+ {
+ if (m_thrown_exceptions_stack.empty ())
+ return nullptr;
+ return &m_thrown_exceptions_stack.back ();
+ }
+ exception_node pop_thrown_exception ()
+ {
+ gcc_assert (!m_thrown_exceptions_stack.empty ());
+ const exception_node retval = m_thrown_exceptions_stack.back ();
+ m_thrown_exceptions_stack.pop_back ();
+ return retval;
+ }
+
+ void push_caught_exception (const exception_node &node)
+ {
+ m_caught_exceptions_stack.push_back (node);
+ }
+ const exception_node *get_current_caught_exception () const
+ {
+ if (m_caught_exceptions_stack.empty ())
+ return nullptr;
+ return &m_caught_exceptions_stack.back ();
+ }
+ exception_node pop_caught_exception ()
+ {
+ gcc_assert (!m_caught_exceptions_stack.empty ());
+ const exception_node retval = m_caught_exceptions_stack.back ();
+ m_caught_exceptions_stack.pop_back ();
+ return retval;
+ }
+
+ bool
+ apply_constraints_for_eh_dispatch_try
+ (const eh_dispatch_try_cfg_superedge &edge,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out);
+
+ bool
+ apply_constraints_for_eh_dispatch_allowed
+ (const eh_dispatch_allowed_cfg_superedge &edge,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out);
+
private:
const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const;
const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const;
@@ -621,9 +706,12 @@ private:
bool apply_constraints_for_ggoto (const cfg_superedge &edge,
const ggoto *goto_stmt,
region_model_context *ctxt);
- bool apply_constraints_for_exception (const gimple *last_stmt,
- region_model_context *ctxt,
- std::unique_ptr<rejected_constraint> *out);
+
+ bool
+ apply_constraints_for_eh_dispatch (const eh_dispatch_cfg_superedge &edge,
+ const geh_dispatch *eh_dispatch_stmt,
+ region_model_context *ctxt,
+ std::unique_ptr<rejected_constraint> *out);
int poison_any_pointers_to_descendents (const region *reg,
enum poison_kind pkind);
@@ -689,6 +777,10 @@ private:
tree callee_fndecl,
region_model_context *ctxt);
+ void check_for_throw_inside_call (const gcall &call,
+ tree fndecl,
+ region_model_context *ctxt);
+
static auto_vec<pop_frame_callback> pop_frame_callbacks;
/* Storing this here to avoid passing it around everywhere. */
region_model_manager *const m_mgr;
@@ -699,6 +791,9 @@ private:
const frame_region *m_current_frame;
+ std::vector<exception_node> m_thrown_exceptions_stack;
+ std::vector<exception_node> m_caught_exceptions_stack;
+
/* Map from base region to size in bytes, for tracking the sizes of
dynamically-allocated regions.
This is part of the region_model rather than the region to allow for
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index f4823c6..de2c330 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -30,9 +30,11 @@ along with GCC; see the file COPYING3. If not see
#include "tree-cfg.h"
#include "tree-dfa.h"
#include "cfganal.h"
+#include "except.h"
#include "analyzer/supergraph.h"
#include "analyzer/analyzer-logging.h"
+#include "analyzer/region-model.h"
#if ENABLE_ANALYZER
@@ -490,21 +492,25 @@ supergraph::add_node (function *fun, basic_block bb, gcall *returning_call,
/* Create a new cfg_superedge from SRC to DEST for the underlying CFG edge E,
adding it to this supergraph.
- If the edge is for a switch statement, create a switch_cfg_superedge
- subclass. */
+ If the edge is for a switch or eh_dispatch statement, create a
+ switch_cfg_superedge or eh_dispatch_cfg_superedge subclass,
+ respectively */
cfg_superedge *
supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e)
{
- /* Special-case switch edges. */
+ /* Special-case switch and eh_dispatch edges. */
gimple *stmt = src->get_last_stmt ();
- cfg_superedge *new_edge;
+ std::unique_ptr<cfg_superedge> new_edge;
if (stmt && stmt->code == GIMPLE_SWITCH)
- new_edge = new switch_cfg_superedge (src, dest, e);
+ new_edge = std::make_unique<switch_cfg_superedge> (src, dest, e);
+ else if (stmt && stmt->code == GIMPLE_EH_DISPATCH)
+ new_edge = eh_dispatch_cfg_superedge::make (src, dest, e,
+ as_a <geh_dispatch *> (stmt));
else
- new_edge = new cfg_superedge (src, dest, e);
- add_edge (new_edge);
- return new_edge;
+ new_edge = std::make_unique<cfg_superedge> (src, dest, e);
+ add_edge (new_edge.get ());
+ return new_edge.release ();
}
/* Create and add a call_superedge representing an interprocedural call
@@ -1009,6 +1015,7 @@ label_text
superedge::get_description (bool user_facing) const
{
pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
dump_label_to_pp (&pp, user_facing);
return label_text::take (xstrdup (pp_formatted_text (&pp)));
}
@@ -1078,6 +1085,8 @@ cfg_superedge::get_phi_arg (const gphi *phi) const
return gimple_phi_arg_def (phi, index);
}
+/* class switch_cfg_superedge : public cfg_superedge. */
+
switch_cfg_superedge::switch_cfg_superedge (supernode *src,
supernode *dst,
::edge e)
@@ -1185,6 +1194,203 @@ switch_cfg_superedge::implicitly_created_default_p () const
return EXPR_LOCATION (case_label) == UNKNOWN_LOCATION;
}
+/* class eh_dispatch_cfg_superedge : public cfg_superedge. */
+
+/* Given an ERT_TRY region, get the eh_catch corresponding to
+ the label of DST_SNODE, if any. */
+
+static eh_catch
+get_catch (eh_region eh_reg, supernode *dst_snode)
+{
+ gcc_assert (eh_reg->type == ERT_TRY);
+
+ tree dst_snode_label = dst_snode->get_label ();
+ if (!dst_snode_label)
+ return nullptr;
+
+ for (eh_catch iter = eh_reg->u.eh_try.first_catch;
+ iter;
+ iter = iter->next_catch)
+ if (iter->label == dst_snode_label)
+ return iter;
+
+ return nullptr;
+}
+
+std::unique_ptr<eh_dispatch_cfg_superedge>
+eh_dispatch_cfg_superedge::make (supernode *src_snode,
+ supernode *dst_snode,
+ ::edge e,
+ const geh_dispatch *eh_dispatch_stmt)
+{
+ const eh_status *eh = src_snode->get_function ()->eh;
+ gcc_assert (eh);
+ int region_idx = gimple_eh_dispatch_region (eh_dispatch_stmt);
+ gcc_assert (region_idx > 0);
+ gcc_assert ((*eh->region_array)[region_idx]);
+ eh_region eh_reg = (*eh->region_array)[region_idx];
+ gcc_assert (eh_reg);
+ switch (eh_reg->type)
+ {
+ default:
+ gcc_unreachable ();
+ case ERT_CLEANUP:
+ // TODO
+ gcc_unreachable ();
+ break;
+ case ERT_TRY:
+ {
+ eh_catch ehc = get_catch (eh_reg, dst_snode);
+ return std::make_unique<eh_dispatch_try_cfg_superedge>
+ (src_snode, dst_snode,
+ e, eh_dispatch_stmt,
+ eh_reg, ehc);
+ }
+ break;
+ case ERT_ALLOWED_EXCEPTIONS:
+ return std::make_unique<eh_dispatch_allowed_cfg_superedge>
+ (src_snode, dst_snode,
+ e, eh_dispatch_stmt,
+ eh_reg);
+ break;
+ case ERT_MUST_NOT_THROW:
+ // TODO
+ gcc_unreachable ();
+ break;
+ }
+}
+
+eh_dispatch_cfg_superedge::
+eh_dispatch_cfg_superedge (supernode *src,
+ supernode *dst,
+ ::edge e,
+ const geh_dispatch *eh_dispatch_stmt,
+ eh_region eh_reg)
+: cfg_superedge (src, dst, e),
+ m_eh_dispatch_stmt (eh_dispatch_stmt),
+ m_eh_region (eh_reg)
+{
+ gcc_assert (m_eh_region);
+}
+
+const eh_status &
+eh_dispatch_cfg_superedge::get_eh_status () const
+{
+ const eh_status *eh = m_src->get_function ()->eh;
+ gcc_assert (eh);
+ return *eh;
+}
+
+// class eh_dispatch_try_cfg_superedge : public eh_dispatch_cfg_superedge
+
+/* Implementation of superedge::dump_label_to_pp for CFG superedges for
+ "eh_dispatch" statements for ERT_TRY regions. */
+
+void
+eh_dispatch_try_cfg_superedge::dump_label_to_pp (pretty_printer *pp,
+ bool user_facing) const
+{
+ if (!user_facing)
+ pp_string (pp, "ERT_TRY: ");
+ if (m_eh_catch)
+ {
+ bool first = true;
+ for (tree iter = m_eh_catch->type_list; iter; iter = TREE_CHAIN (iter))
+ {
+ if (!first)
+ pp_string (pp, ", ");
+ pp_printf (pp, "on catch %qT", TREE_VALUE (iter));
+ first = false;
+ }
+ }
+ else
+ pp_string (pp, "on uncaught exception");
+}
+
+bool
+eh_dispatch_try_cfg_superedge::
+apply_constraints (region_model *model,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out) const
+{
+ return model->apply_constraints_for_eh_dispatch_try
+ (*this, ctxt, exception_type, out);
+}
+
+// class eh_dispatch_allowed_cfg_superedge : public eh_dispatch_cfg_superedge
+
+eh_dispatch_allowed_cfg_superedge::
+eh_dispatch_allowed_cfg_superedge (supernode *src, supernode *dst, ::edge e,
+ const geh_dispatch *eh_dispatch_stmt,
+ eh_region eh_reg)
+: eh_dispatch_cfg_superedge (src, dst, e, eh_dispatch_stmt, eh_reg)
+{
+ gcc_assert (eh_reg->type == ERT_ALLOWED_EXCEPTIONS);
+
+ /* We expect two sibling out-edges at an eh_dispatch from such a region:
+
+ - one to a bb without a gimple label, with a resx,
+ for exceptions of expected types
+
+ - one to a bb with a gimple label, with a call to __cxa_unexpected,
+ for exceptions of unexpected types.
+
+ Set m_kind for this edge accordingly. */
+ gcc_assert (e->src->succs->length () == 2);
+ tree label_for_unexpected_exceptions = eh_reg->u.allowed.label;
+ tree label_for_dest_enode = dst->get_label ();
+ if (label_for_dest_enode == label_for_unexpected_exceptions)
+ m_kind = eh_kind::unexpected;
+ else
+ {
+ gcc_assert (label_for_dest_enode == nullptr);
+ m_kind = eh_kind::expected;
+ }
+}
+
+void
+eh_dispatch_allowed_cfg_superedge::dump_label_to_pp (pretty_printer *pp,
+ bool user_facing) const
+{
+ if (!user_facing)
+ {
+ switch (m_kind)
+ {
+ default:
+ gcc_unreachable ();
+ case eh_dispatch_allowed_cfg_superedge::eh_kind::expected:
+ pp_string (pp, "expected: ");
+ break;
+ case eh_dispatch_allowed_cfg_superedge::eh_kind::unexpected:
+ pp_string (pp, "unexpected: ");
+ break;
+ }
+ pp_string (pp, "ERT_ALLOWED_EXCEPTIONS: ");
+ eh_region eh_reg = get_eh_region ();
+ bool first = true;
+ for (tree iter = eh_reg->u.allowed.type_list; iter;
+ iter = TREE_CHAIN (iter))
+ {
+ if (!first)
+ pp_string (pp, ", ");
+ pp_printf (pp, "%qT", TREE_VALUE (iter));
+ first = false;
+ }
+ }
+}
+
+bool
+eh_dispatch_allowed_cfg_superedge::
+apply_constraints (region_model *model,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out) const
+{
+ return model->apply_constraints_for_eh_dispatch_allowed
+ (*this, ctxt, exception_type, out);
+}
+
/* Implementation of superedge::dump_label_to_pp for interprocedural
superedges. */
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index ddf721b..8796ab7 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h"
#include "gimple-iterator.h"
#include "digraph.h"
+#include "except.h"
using namespace ana;
@@ -42,6 +43,9 @@ class superedge;
class return_superedge;
class cfg_superedge;
class switch_cfg_superedge;
+ class eh_dispatch_cfg_superedge;
+ class eh_dispatch_try_cfg_superedge;
+ class eh_dispatch_allowed_cfg_superedge;
class supercluster;
class dot_annotator;
@@ -330,6 +334,9 @@ class superedge : public dedge<supergraph_traits>
virtual cfg_superedge *dyn_cast_cfg_superedge () { return NULL; }
virtual const cfg_superedge *dyn_cast_cfg_superedge () const { return NULL; }
virtual const switch_cfg_superedge *dyn_cast_switch_cfg_superedge () const { return NULL; }
+ virtual const eh_dispatch_cfg_superedge *dyn_cast_eh_dispatch_cfg_superedge () const { return nullptr; }
+ virtual const eh_dispatch_try_cfg_superedge *dyn_cast_eh_dispatch_try_cfg_superedge () const { return nullptr; }
+ virtual const eh_dispatch_allowed_cfg_superedge *dyn_cast_eh_dispatch_allowed_cfg_superedge () const { return nullptr; }
virtual callgraph_superedge *dyn_cast_callgraph_superedge () { return NULL; }
virtual const callgraph_superedge *dyn_cast_callgraph_superedge () const { return NULL; }
virtual call_superedge *dyn_cast_call_superedge () { return NULL; }
@@ -592,6 +599,164 @@ is_a_helper <const switch_cfg_superedge *>::test (const superedge *sedge)
namespace ana {
+/* A subclass for edges from eh_dispatch statements, retaining enough
+ information to identify the various types being caught, vs the
+ "unhandled type" case, and for adding labels when rendering
+ via graphviz.
+ This is abstract; there are concrete subclasses based on the type
+ of the eh_region. */
+
+class eh_dispatch_cfg_superedge : public cfg_superedge
+{
+ public:
+ static std::unique_ptr<eh_dispatch_cfg_superedge>
+ make (supernode *src,
+ supernode *dest,
+ ::edge e,
+ const geh_dispatch *eh_dispatch_stmt);
+
+ const eh_dispatch_cfg_superedge *dyn_cast_eh_dispatch_cfg_superedge () const
+ final override
+ {
+ return this;
+ }
+
+ const geh_dispatch *
+ get_eh_dispatch_stmt () const
+ {
+ return m_eh_dispatch_stmt;
+ }
+
+ const eh_status &get_eh_status () const;
+ eh_region get_eh_region () const { return m_eh_region; }
+
+ virtual bool
+ apply_constraints (region_model *model,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out) const = 0;
+
+protected:
+ eh_dispatch_cfg_superedge (supernode *src, supernode *dst, ::edge e,
+ const geh_dispatch *eh_dispatch_stmt,
+ eh_region eh_reg);
+
+private:
+ const geh_dispatch *m_eh_dispatch_stmt;
+ eh_region m_eh_region;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const eh_dispatch_cfg_superedge *>::test (const superedge *sedge)
+{
+ return sedge->dyn_cast_eh_dispatch_cfg_superedge () != NULL;
+}
+
+namespace ana {
+
+/* A concrete subclass for edges from an eh_dispatch statements
+ for ERT_TRY regions. */
+
+class eh_dispatch_try_cfg_superedge : public eh_dispatch_cfg_superedge
+{
+ public:
+ eh_dispatch_try_cfg_superedge (supernode *src, supernode *dst, ::edge e,
+ const geh_dispatch *eh_dispatch_stmt,
+ eh_region eh_reg,
+ eh_catch ehc)
+ : eh_dispatch_cfg_superedge (src, dst, e, eh_dispatch_stmt, eh_reg),
+ m_eh_catch (ehc)
+ {
+ gcc_assert (eh_reg->type == ERT_TRY);
+ }
+
+ const eh_dispatch_try_cfg_superedge *
+ dyn_cast_eh_dispatch_try_cfg_superedge () const final override
+ {
+ return this;
+ }
+
+ void dump_label_to_pp (pretty_printer *pp,
+ bool user_facing) const final override;
+
+ eh_catch get_eh_catch () const { return m_eh_catch; }
+
+ bool
+ apply_constraints (region_model *model,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out)
+ const final override;
+
+private:
+ eh_catch m_eh_catch;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const eh_dispatch_try_cfg_superedge *>::test (const superedge *sedge)
+{
+ return sedge->dyn_cast_eh_dispatch_try_cfg_superedge () != NULL;
+}
+
+namespace ana {
+
+/* A concrete subclass for edges from an eh_dispatch statements
+ for ERT_ALLOWED_EXCEPTIONS regions. */
+
+class eh_dispatch_allowed_cfg_superedge : public eh_dispatch_cfg_superedge
+{
+ public:
+ enum eh_kind
+ {
+ expected,
+ unexpected
+ };
+
+ eh_dispatch_allowed_cfg_superedge (supernode *src, supernode *dst, ::edge e,
+ const geh_dispatch *eh_dispatch_stmt,
+ eh_region eh_reg);
+
+ const eh_dispatch_allowed_cfg_superedge *
+ dyn_cast_eh_dispatch_allowed_cfg_superedge () const final override
+ {
+ return this;
+ }
+
+ void dump_label_to_pp (pretty_printer *pp,
+ bool user_facing) const final override;
+
+ bool
+ apply_constraints (region_model *model,
+ region_model_context *ctxt,
+ tree exception_type,
+ std::unique_ptr<rejected_constraint> *out)
+ const final override;
+
+ enum eh_kind get_eh_kind () const { return m_kind; }
+
+private:
+ enum eh_kind m_kind;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const eh_dispatch_allowed_cfg_superedge *>::test (const superedge *sedge)
+{
+ return sedge->dyn_cast_eh_dispatch_allowed_cfg_superedge () != NULL;
+}
+
+namespace ana {
/* Base class for adding additional content to the .dot output
for a supergraph. */
diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc
index 513d9cc..f3f80d1 100644
--- a/gcc/analyzer/svalue.cc
+++ b/gcc/analyzer/svalue.cc
@@ -860,6 +860,19 @@ svalue::maybe_get_deref_base_region () const
}
}
+/* If this svalue is a pointer to the typeinfo instance for a particular
+ type, return that type. Otherwise return NULL_TREE. */
+
+tree
+svalue::maybe_get_type_from_typeinfo () const
+{
+ if (const region *reg = maybe_get_region ())
+ if (const decl_region *decl_reg = reg->dyn_cast_decl_region ())
+ return TREE_TYPE (DECL_NAME (decl_reg->get_decl ()));
+
+ return NULL_TREE;
+}
+
/* class region_svalue : public svalue. */
/* Implementation of svalue::dump_to_pp vfunc for region_svalue. */
diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h
index f416c57..7a27cb6 100644
--- a/gcc/analyzer/svalue.h
+++ b/gcc/analyzer/svalue.h
@@ -188,6 +188,8 @@ public:
const region_model &model,
const svalue *outer_sval = nullptr) const;
+ tree maybe_get_type_from_typeinfo () const;
+
protected:
svalue (complexity c, symbol::id_t id, tree type)
: symbol (c, id), m_type (type)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7fcf7de..d5a2bf6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -513,6 +513,7 @@ Objective-C and Objective-C++ Dialects}.
-Wno-analyzer-tainted-divisor
-Wno-analyzer-tainted-offset
-Wno-analyzer-tainted-size
+-Wno-analyzer-throw-of-unexpected-type
-Wanalyzer-symbol-too-complex
-Wanalyzer-too-complex
-Wno-analyzer-undefined-behavior-ptrdiff
@@ -11008,6 +11009,7 @@ Enabling this option effectively enables the following warnings:
-Wanalyzer-tainted-divisor
-Wanalyzer-tainted-offset
-Wanalyzer-tainted-size
+-Wanalyzer-throw-of-unexpected-type
-Wanalyzer-undefined-behavior-ptrdiff
-Wanalyzer-undefined-behavior-strtok
-Wanalyzer-unsafe-call-within-signal-handler
@@ -11659,6 +11661,17 @@ attacker could inject an out-of-bounds access.
See @uref{https://cwe.mitre.org/data/definitions/129.html, CWE-129: Improper Validation of Array Index}.
+@opindex Wanalyzer-throw-of-unexpected-type
+@opindex Wno-analyzer-throw-of-unexpected-type
+@item -Wno-analyzer-throw-of-unexpected-type
+This warning requires @option{-fanalyzer} which enables it;
+use @option{-Wno-analyzer-throw-of-unexpected-type} to disable it.
+Dynamic exception specifications are only available in C++14 and earlier.
+
+This diagnostic warns for paths through the code in which a an exception
+is thrown from a function with a dynamic exception specification where
+the exception does not comply with the specification.
+
@opindex Wanalyzer-undefined-behavior-ptrdiff
@opindex Wno-analyzer-undefined-behavior-ptrdiff
@item -Wno-analyzer-undefined-behavior-ptrdiff
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 7e3086f..032365f 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -3561,7 +3561,7 @@ gimple_call_set_nothrow (gcall *s, bool nothrow_p)
/* Return true if S is a nothrow call. */
inline bool
-gimple_call_nothrow_p (gcall *s)
+gimple_call_nothrow_p (const gcall *s)
{
return (gimple_call_flags (s) & ECF_NOTHROW) != 0;
}
diff --git a/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-2a.c b/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-2a.c
index cf014b0..175d9c3 100644
--- a/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-2a.c
+++ b/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-2a.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-verbosity=2" } */
+/* { dg-additional-options "-fanalyzer-verbosity=2 -fno-exceptions" } */
typedef struct FILE FILE;
diff --git a/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-3a.c b/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-3a.c
index b0ece20..8d66c8f 100644
--- a/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-3a.c
+++ b/gcc/testsuite/c-c++-common/analyzer/analyzer-verbosity-3a.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-verbosity=3" } */
+/* { dg-additional-options "-fanalyzer-verbosity=3 -fno-exceptions" } */
typedef struct FILE FILE;
diff --git a/gcc/testsuite/c-c++-common/analyzer/attr-const-2.c b/gcc/testsuite/c-c++-common/analyzer/attr-const-2.c
index ab79514..5329a89 100644
--- a/gcc/testsuite/c-c++-common/analyzer/attr-const-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/attr-const-2.c
@@ -1,5 +1,5 @@
extern int const_p (int) __attribute__((const));
-extern void do_stuff (void);
+extern void do_stuff (void) __attribute__((nothrow));
void test (int a)
{
diff --git a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-4.c b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-4.c
index 1517667..1e2e1f9 100644
--- a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-4.c
+++ b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-4.c
@@ -2,7 +2,7 @@
struct foo;
extern void foo_release (struct foo *)
- __attribute__((nonnull));
+ __attribute__((nonnull, nothrow));
extern struct foo *foo_acquire (void)
__attribute__ ((malloc (foo_release)));
diff --git a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-5.c b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-5.c
index 7ff4e57..8b7ffc1 100644
--- a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-5.c
+++ b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-5.c
@@ -1,7 +1,7 @@
/* Example of extra argument to "malloc" attribute. */
struct foo;
-extern void foo_release (int, struct foo *);
+extern void foo_release (int, struct foo *) __attribute__((nothrow));
extern struct foo *foo_acquire (void)
__attribute__ ((malloc (foo_release, 2)));
diff --git a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-6.c b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-6.c
index 1665d41..45ee406f 100644
--- a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-6.c
+++ b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-6.c
@@ -1,4 +1,5 @@
/* Adapted from gcc.dg/Wmismatched-dealloc.c. */
+/* { dg-additional-options "-fno-exceptions" } */
#define A(...) __attribute__ ((malloc (__VA_ARGS__)))
diff --git a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c
index 87ad42a..fd51630 100644
--- a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c
+++ b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c
@@ -1,6 +1,8 @@
/* Adapted from linux 5.3.11: drivers/net/wireless/ath/ath10k/usb.c
Reduced reproducer for CVE-2019-19078 (leak of struct urb). */
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef unsigned char u8;
typedef unsigned short u16;
diff --git a/gcc/testsuite/c-c++-common/analyzer/attr-malloc-exception.c b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-exception.c
new file mode 100644
index 0000000..9f5e2e8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/attr-malloc-exception.c
@@ -0,0 +1,17 @@
+/* { dg-additional-options "-fexceptions" } */
+
+extern void free (void *);
+
+/* Not marked "nothrow", so assume it could throw. */
+char *xstrdup (const char *)
+ __attribute__((malloc (free), returns_nonnull));
+
+void test_1 (const char *s, const char *t)
+{
+ char *p = xstrdup (s); /* { dg-message "allocated here" } */
+ char *q = xstrdup (t); /* { dg-warning "leak of 'p'" } */
+ /* { dg-message "if .* throws an exception\.\.\." "" { target *-*-* } .-1 } */
+
+ free (q);
+ free (p);
+}
diff --git a/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c b/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c
index b395623..a15ab65 100644
--- a/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
+/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex -fno-exceptions" } */
/* { dg-skip-if "c++98 has no noreturn attribute" { c++98_only } } */
#ifdef __cplusplus
diff --git a/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158.c b/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158.c
index de70583..6fbbce4 100644
--- a/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158.c
+++ b/gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-symbol-too-complex" } */
+/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-symbol-too-complex -fno-exceptions" } */
typedef __SIZE_TYPE__ size_t;
enum { _ISspace = ((5) < 8 ? ((1 << (5)) << 8) : ((1 << (5)) >> 8)) };
diff --git a/gcc/testsuite/c-c++-common/analyzer/capacity-2.c b/gcc/testsuite/c-c++-common/analyzer/capacity-2.c
index 7d2de4e..3846239 100644
--- a/gcc/testsuite/c-c++-common/analyzer/capacity-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/capacity-2.c
@@ -1,5 +1,6 @@
/* { dg-skip-if "" { powerpc*-*-aix* } } */
/* { dg-skip-if "requires hosted libstdc++ for stdlib size_t" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
#include "analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/coreutils-sum-pr108666.c b/gcc/testsuite/c-c++-common/analyzer/coreutils-sum-pr108666.c
index c41b61d..95091e7 100644
--- a/gcc/testsuite/c-c++-common/analyzer/coreutils-sum-pr108666.c
+++ b/gcc/testsuite/c-c++-common/analyzer/coreutils-sum-pr108666.c
@@ -1,5 +1,7 @@
/* Reduced from coreutils's sum.c: bsd_sum_stream */
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef __SIZE_TYPE__ size_t;
typedef unsigned char __uint8_t;
typedef unsigned long int __uintmax_t;
diff --git a/gcc/testsuite/c-c++-common/analyzer/data-model-22.c b/gcc/testsuite/c-c++-common/analyzer/data-model-22.c
index 8429b2f..65bf346 100644
--- a/gcc/testsuite/c-c++-common/analyzer/data-model-22.c
+++ b/gcc/testsuite/c-c++-common/analyzer/data-model-22.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
#include <string.h>
#include "analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/data-model-5d.c b/gcc/testsuite/c-c++-common/analyzer/data-model-5d.c
index a86d506..bb45917 100644
--- a/gcc/testsuite/c-c++-common/analyzer/data-model-5d.c
+++ b/gcc/testsuite/c-c++-common/analyzer/data-model-5d.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
/* A toy re-implementation of CPython's object model. */
diff --git a/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c b/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c
index 7431bd1..b790997 100644
--- a/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c
+++ b/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108455-git-pack-revindex.c
@@ -1,4 +1,5 @@
/* Reduced from git-2.39.0's pack-revindex.c */
+/* { dg-additional-options "-fno-exceptions" } */
typedef unsigned int __uint32_t;
typedef unsigned long int __uintmax_t;
diff --git a/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c b/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c
index 7123cf5..a7f8049 100644
--- a/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c
+++ b/gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr108475-haproxy-tcpcheck.c
@@ -1,6 +1,7 @@
/* Reduced from haproxy-2.7.1: src/tcpcheck.c. */
/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-fno-exceptions" } */
typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/c-c++-common/analyzer/edges-2.c b/gcc/testsuite/c-c++-common/analyzer/edges-2.c
index 7e4543c..df4bfaa 100644
--- a/gcc/testsuite/c-c++-common/analyzer/edges-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/edges-2.c
@@ -1,5 +1,7 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
+
#include <stdlib.h>
int foo ();
diff --git a/gcc/testsuite/c-c++-common/analyzer/fd-2.c b/gcc/testsuite/c-c++-common/analyzer/fd-2.c
index 10c9ecd..b6b0a57 100644
--- a/gcc/testsuite/c-c++-common/analyzer/fd-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/fd-2.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
int open(const char *, int mode);
void close(int fd);
#define O_RDONLY 0
@@ -61,4 +63,4 @@ test_5 (const char *path, mode_t mode)
int fd = creat (path, mode);
close(fd);
close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
-} \ No newline at end of file
+}
diff --git a/gcc/testsuite/c-c++-common/analyzer/fd-3.c b/gcc/testsuite/c-c++-common/analyzer/fd-3.c
index 8e71b14..4894b64 100644
--- a/gcc/testsuite/c-c++-common/analyzer/fd-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/fd-3.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
int open(const char *, int mode);
void close(int fd);
int write (int fd, void *buf, int nbytes);
diff --git a/gcc/testsuite/c-c++-common/analyzer/fd-meaning.c b/gcc/testsuite/c-c++-common/analyzer/fd-meaning.c
index 6a9ec92..bd0d458a 100644
--- a/gcc/testsuite/c-c++-common/analyzer/fd-meaning.c
+++ b/gcc/testsuite/c-c++-common/analyzer/fd-meaning.c
@@ -1,4 +1,5 @@
- /* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
+/* { dg-additional-options "-fno-exceptions" } */
+/* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
int open(const char *, int mode);
void close(int fd);
@@ -34,4 +35,4 @@ void test_3 (const char* path)
close(fd); /* { dg-message "meaning: \\{verb: 'release', noun: 'resource'\\}" } */
close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" } */
}
-} \ No newline at end of file
+}
diff --git a/gcc/testsuite/c-c++-common/analyzer/file-1.c b/gcc/testsuite/c-c++-common/analyzer/file-1.c
index 316cbb3..e87cf73 100644
--- a/gcc/testsuite/c-c++-common/analyzer/file-1.c
+++ b/gcc/testsuite/c-c++-common/analyzer/file-1.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef struct FILE FILE;
FILE* fopen (const char*, const char*);
diff --git a/gcc/testsuite/c-c++-common/analyzer/file-3.c b/gcc/testsuite/c-c++-common/analyzer/file-3.c
index 8f93a98..ca992bf 100644
--- a/gcc/testsuite/c-c++-common/analyzer/file-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/file-3.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef struct _IO_FILE FILE;
extern struct _IO_FILE *stderr;
diff --git a/gcc/testsuite/c-c++-common/analyzer/file-meaning-1.c b/gcc/testsuite/c-c++-common/analyzer/file-meaning-1.c
index 66b72a7..c9aee5e 100644
--- a/gcc/testsuite/c-c++-common/analyzer/file-meaning-1.c
+++ b/gcc/testsuite/c-c++-common/analyzer/file-meaning-1.c
@@ -1,3 +1,4 @@
+/* { dg-additional-options "-fno-exceptions" } */
/* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
typedef struct FILE FILE;
diff --git a/gcc/testsuite/c-c++-common/analyzer/infinite-recursion.c b/gcc/testsuite/c-c++-common/analyzer/infinite-recursion.c
index 6b7d25c..bbbb68e 100644
--- a/gcc/testsuite/c-c++-common/analyzer/infinite-recursion.c
+++ b/gcc/testsuite/c-c++-common/analyzer/infinite-recursion.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
extern void marker_A(void);
extern void marker_B(void);
extern void marker_C(void);
diff --git a/gcc/testsuite/c-c++-common/analyzer/leak-3.c b/gcc/testsuite/c-c++-common/analyzer/leak-3.c
index a386d88..19d7501 100644
--- a/gcc/testsuite/c-c++-common/analyzer/leak-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/leak-3.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-dedupe-1.c b/gcc/testsuite/c-c++-common/analyzer/malloc-dedupe-1.c
index 8653c67..c296061 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-dedupe-1.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-dedupe-1.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-in-loop.c b/gcc/testsuite/c-c++-common/analyzer/malloc-in-loop.c
index e7179f0..0d86801 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-in-loop.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-in-loop.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
#include "../../gcc.dg/analyzer/analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-many-paths-3.c b/gcc/testsuite/c-c++-common/analyzer/malloc-many-paths-3.c
index 6ee30f3..5daa696 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-many-paths-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-many-paths-3.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-paths-5.c b/gcc/testsuite/c-c++-common/analyzer/malloc-paths-5.c
index f03f978..4a1870d 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-paths-5.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-paths-5.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdio.h>
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-paths-7.c b/gcc/testsuite/c-c++-common/analyzer/malloc-paths-7.c
index 766bbe7..12b93b2 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-paths-7.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-paths-7.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-paths-8.c b/gcc/testsuite/c-c++-common/analyzer/malloc-paths-8.c
index 77e3e02..8af84ed 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-paths-8.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-paths-8.c
@@ -1,3 +1,4 @@
+/* { dg-additional-options "-fno-exceptions" } */
/* { dg-additional-options "-fanalyzer-transitivity" } */
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-1a.c b/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-1a.c
index 4e40833..ffb0ffe 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-1a.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-1a.c
@@ -1,3 +1,4 @@
+/* { dg-additional-options "-fno-exceptions" } */
/* { dg-additional-options "-fno-analyzer-call-summaries -fanalyzer-transitivity" } */
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-2.c b/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-2.c
index 36ec510..052b401 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-2.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
#include "analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-3.c b/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-3.c
index 70b3edd..258706c 100644
--- a/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/malloc-vs-local-3.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
#include "analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/paths-7.c b/gcc/testsuite/c-c++-common/analyzer/paths-7.c
index 2743de5..f7c5cbf 100644
--- a/gcc/testsuite/c-c++-common/analyzer/paths-7.c
+++ b/gcc/testsuite/c-c++-common/analyzer/paths-7.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
#include "analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr110830.c b/gcc/testsuite/c-c++-common/analyzer/pr110830.c
index f5a39b7..ebfd38d 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr110830.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr110830.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef __SIZE_TYPE__ size_t;
void free(void *);
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr93032-mztools-simplified.c b/gcc/testsuite/c-c++-common/analyzer/pr93032-mztools-simplified.c
index 4a08f0f1..e389e43 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr93032-mztools-simplified.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr93032-mztools-simplified.c
@@ -1,4 +1,5 @@
/* { dg-do "compile" } */
+/* { dg-additional-options "-fno-exceptions" } */
/* Minimal replacement of system headers. */
#define NULL ((void *) 0)
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-feasibility-3.c b/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-feasibility-3.c
index 50d3388..19a3023 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-feasibility-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-feasibility-3.c
@@ -3,6 +3,7 @@
Adapted from intl/localealias.c, with all #includes removed. */
/* { dg-do "compile" } */
+/* { dg-additional-options "-fno-exceptions" } */
/* Handle aliases for locale names.
Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-simplified.c b/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-simplified.c
index 6f65add..45517468 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-simplified.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr93355-localealias-simplified.c
@@ -3,6 +3,7 @@
Adapted from intl/localealias.c, with all #includes removed. */
/* { dg-do "compile" } */
+/* { dg-additional-options "-fno-exceptions" } */
/* Handle aliases for locale names.
Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr96650-1-trans.c b/gcc/testsuite/c-c++-common/analyzer/pr96650-1-trans.c
index b20630b..fb194ad 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr96650-1-trans.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr96650-1-trans.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2 -fanalyzer-transitivity" } */
+/* { dg-additional-options "-fno-exceptions" } */
int *wf;
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr97072.c b/gcc/testsuite/c-c++-common/analyzer/pr97072.c
index 4024124..82411a1 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr97072.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr97072.c
@@ -1,4 +1,4 @@
-void unknown_fn_1 (void *);
+void unknown_fn_1 (void *) __attribute__((nothrow));
void test_1 (int co, int y)
{
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr98575-1.c b/gcc/testsuite/c-c++-common/analyzer/pr98575-1.c
index 6472e76..b8ddf77c 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr98575-1.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr98575-1.c
@@ -4,7 +4,7 @@
void **g;
-extern void unknown_fn (void);
+extern void unknown_fn (void) __attribute__((nothrow));
/* Without a call to unknown_fn. */
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr99716-1.c b/gcc/testsuite/c-c++-common/analyzer/pr99716-1.c
index 41be8ca..60f3a598 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr99716-1.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr99716-1.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef struct FILE FILE;
FILE* fopen (const char*, const char*);
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr99716-2.c b/gcc/testsuite/c-c++-common/analyzer/pr99716-2.c
index ef7cc5f..caf5c86 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr99716-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr99716-2.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib rand" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
/* Reduced from
https://github.com/libguestfs/libguestfs/blob/e0a11061035d47b118c95706240bcc17fd576edc/tests/mount-local/test-parallel-mount-local.c#L299-L335
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr99716-3.c b/gcc/testsuite/c-c++-common/analyzer/pr99716-3.c
index 414d57e..98f656f 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr99716-3.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr99716-3.c
@@ -1,4 +1,5 @@
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/pragma-2.c b/gcc/testsuite/c-c++-common/analyzer/pragma-2.c
index bd96a25..f309876 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pragma-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pragma-2.c
@@ -1,6 +1,7 @@
/* { dg-skip-if "" { powerpc*-*-aix* } } */
/* Verify that we can disable -Wanalyzer-too-complex via pragmas. */
/* { dg-additional-options "-Wanalyzer-too-complex -Werror=analyzer-too-complex -fno-analyzer-state-merge -g" } */
+/* { dg-additional-options "-fno-exceptions" } */
/* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib } } */
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/rhbz1878600.c b/gcc/testsuite/c-c++-common/analyzer/rhbz1878600.c
index 9f6ccb6..9606044 100644
--- a/gcc/testsuite/c-c++-common/analyzer/rhbz1878600.c
+++ b/gcc/testsuite/c-c++-common/analyzer/rhbz1878600.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
#include <stdio.h>
#define INI_MAX_LINE 200
diff --git a/gcc/testsuite/c-c++-common/analyzer/strndup-1.c b/gcc/testsuite/c-c++-common/analyzer/strndup-1.c
index 3f90afe..915f220 100644
--- a/gcc/testsuite/c-c++-common/analyzer/strndup-1.c
+++ b/gcc/testsuite/c-c++-common/analyzer/strndup-1.c
@@ -1,6 +1,7 @@
/* { dg-skip-if "no strndup in libc" { *-*-darwin[789]* *-*-darwin10* hppa*-*-hpux* *-*-mingw* *-*-vxworks* } } */
/* { dg-additional-options "-D_POSIX_C_SOURCE=200809L" } */
/* { dg-skip-if "requires hosted libstdc++ for stdlib free" { ! hostedlib } } */
+/* { dg-additional-options "-fno-exceptions" } */
#include <string.h>
#include <stdlib.h>
diff --git a/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4-disabled.c b/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4-disabled.c
index 868c393..1aa4159e 100644
--- a/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4-disabled.c
+++ b/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4-disabled.c
@@ -2,6 +2,7 @@
region_model_context_decorator::add_note. */
/* { dg-additional-options "-Wno-analyzer-write-to-string-literal" } */
+/* { dg-additional-options "-fno-exceptions" } */
typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4.c b/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4.c
index 971e8f3..b60fba0 100644
--- a/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4.c
+++ b/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-4.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
typedef __SIZE_TYPE__ size_t;
int getrandom (void *__buffer, size_t __length, /* { dg-line getrandom } */
diff --git a/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-5.c b/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-5.c
index 2ecad8c..78b2204 100644
--- a/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-5.c
+++ b/gcc/testsuite/c-c++-common/analyzer/write-to-string-literal-5.c
@@ -2,6 +2,7 @@
notes) works. */
/* { dg-additional-options "-fanalyzer-show-duplicate-count" } */
+/* { dg-additional-options "-fno-exceptions" } */
#include "../../gcc.dg/analyzer/analyzer-decls.h"
diff --git a/gcc/testsuite/c-c++-common/analyzer/zlib-5.c b/gcc/testsuite/c-c++-common/analyzer/zlib-5.c
index 1e3746d..fa82e43 100644
--- a/gcc/testsuite/c-c++-common/analyzer/zlib-5.c
+++ b/gcc/testsuite/c-c++-common/analyzer/zlib-5.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O3" } */
+/* { dg-additional-options "-fno-exceptions" } */
#include "analyzer-decls.h"
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-could-throw-1.C b/gcc/testsuite/g++.dg/analyzer/exception-could-throw-1.C
new file mode 100644
index 0000000..4671e62
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-could-throw-1.C
@@ -0,0 +1,37 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+extern void do_something ();
+extern void do_something_nothrow () __attribute__ ((nothrow));;
+
+int test ()
+{
+ try
+ {
+ do_something ();
+ }
+ catch (int i)
+ {
+ int j = i;
+ __analyzer_eval (i == 42); // { dg-warning "UNKNOWN" }
+ __analyzer_eval (i == j); // { dg-warning "TRUE" }
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 1;
+ }
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 0;
+}
+
+int test_nothrow ()
+{
+ try
+ {
+ do_something_nothrow ();
+ }
+ catch (int i)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-could-throw-2.C b/gcc/testsuite/g++.dg/analyzer/exception-could-throw-2.C
new file mode 100644
index 0000000..6572113
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-could-throw-2.C
@@ -0,0 +1,32 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+extern void do_something ();
+
+int test ()
+{
+ try
+ {
+ do_something ();
+ }
+ catch (const io_error &err)
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 1;
+ }
+ catch (const value_error &err)
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 2;
+ }
+ catch (const runtime_error &err)
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 3;
+ }
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C b/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C
new file mode 100644
index 0000000..9847203
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-dynamic-spec.C
@@ -0,0 +1,62 @@
+// Tests of dynamic exception specifications
+// { dg-require-effective-target c++14_down }
+// { dg-prune-output "dynamic exception specifications are deprecated" }
+
+struct io_error {};
+struct file_io_error : public io_error {};
+struct mem_error {};
+
+// Valid intraprocedural:
+
+void test_1 (int flag) throw (io_error)
+{
+ if (flag)
+ throw io_error();
+}
+
+// Invalid intraprocedural:
+
+void test_2 (int flag) throw (io_error) // { dg-warning "throwing exception of unexpected type 'mem_error' from 'test_2'" }
+// { dg-message "exception of unexpected type 'mem_error' thrown from 'test_2'" "" { target *-*-* } .-1 }
+// { dg-message "'test_2' declared here" "" { target *-*-* } .-2 }
+{
+ if (flag)
+ throw mem_error(); // { dg-message "throwing exception of type 'mem_error' here\.\.\." }
+}
+
+// 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 *-*-* } }
+{
+ if (flag)
+ throw file_io_error();
+}
+
+// Valid interprocedural:
+
+void test_4_inner (int flag)
+{
+ if (flag)
+ throw io_error ();
+}
+
+void test_4_outer (int flag) throw (io_error)
+{
+ test_4_inner (flag);
+}
+
+// Invalid interprocedural:
+
+void test_5_inner (int flag)
+{
+ if (flag)
+ throw mem_error (); // { dg-message "throwing exception of type 'mem_error' here\.\.\." }
+ // { dg-message "unwinding stack frame" "" { target *-*-* } .-1 }
+}
+
+void test_5_outer (int flag) throw (io_error) // { dg-warning "throwing exception of unexpected type 'mem_error' from 'test_5_outer'" }
+// { dg-message "exception of unexpected type 'mem_error' thrown from 'test_5_outer'" "" { target *-*-* } .-1 }
+// { dg-message "'test_5_outer' declared here" "" { target *-*-* } .-2 }
+{
+ test_5_inner (flag);
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-leak-1.C b/gcc/testsuite/g++.dg/analyzer/exception-leak-1.C
new file mode 100644
index 0000000..25467ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-leak-1.C
@@ -0,0 +1,8 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ void *ptr = __builtin_malloc (1024); // { dg-message "allocated here" }
+
+ throw 42; // { dg-warning "leak of 'ptr'" }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-leak-2.C b/gcc/testsuite/g++.dg/analyzer/exception-leak-2.C
new file mode 100644
index 0000000..0e0eef8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-leak-2.C
@@ -0,0 +1,18 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ void *ptr = __builtin_malloc (1024); // { dg-message "allocated here" }
+
+ try
+ {
+ throw 42;
+ }
+ catch (int i) // { dg-message "\.\.\.catching exception of type 'int' here" }
+ {
+ return -1;
+ } // { dg-warning "leak of 'ptr'" }
+
+ __builtin_free (ptr);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-leak-3.C b/gcc/testsuite/g++.dg/analyzer/exception-leak-3.C
new file mode 100644
index 0000000..3684d66
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-leak-3.C
@@ -0,0 +1,12 @@
+extern void do_something ();
+
+int test ()
+{
+ void *ptr = __builtin_malloc (1024); // { dg-message "allocated here" }
+
+ do_something (); // { dg-message "if 'void do_something\\(\\)' throws an exception\.\.\." }
+ // { dg-warning "leak of 'ptr'" "" { target *-*-* } .-1 }
+
+ __builtin_free (ptr);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-leak-4.C b/gcc/testsuite/g++.dg/analyzer/exception-leak-4.C
new file mode 100644
index 0000000..7280d32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-leak-4.C
@@ -0,0 +1,16 @@
+static void
+do_something (int flag)
+{
+ if (flag)
+ throw 42; // { dg-warning "leak of 'ptr'" }
+}
+
+int test (int flag)
+{
+ void *ptr = __builtin_malloc (1024); // { dg-message "allocated here" }
+
+ do_something (flag);
+
+ __builtin_free (ptr);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-leak-5.C b/gcc/testsuite/g++.dg/analyzer/exception-leak-5.C
new file mode 100644
index 0000000..d99e53c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-leak-5.C
@@ -0,0 +1,51 @@
+/* Verify that we detect a leak when unwinding multiple frames. */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+int inner (int flag)
+{
+ if (flag)
+ throw value_error (); // { dg-warning "leak" }
+ // { dg-message "throwing exception of type 'value_error' here\.\.\." "" { target *-*-* } .-1 }
+
+ return 0;
+}
+
+int __analyzer_middle (int flag)
+{
+ void *ptr = __builtin_malloc (1024); // { dg-message "allocated here" }
+
+ int rval = inner (flag);
+
+ __builtin_free (ptr);
+
+ return rval;
+}
+
+int outer ()
+{
+ try
+ {
+ __analyzer_middle (1);
+ }
+ catch (const io_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ catch (const value_error &err)
+ {
+ return 2;
+ }
+ catch (const runtime_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 3;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-leak-6.C b/gcc/testsuite/g++.dg/analyzer/exception-leak-6.C
new file mode 100644
index 0000000..cb70230
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-leak-6.C
@@ -0,0 +1,22 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+extern void do_something (int x);
+
+int inner (int x)
+{
+ do_something (x); // { dg-warning "leak" }
+ // { dg-message "if 'void do_something\\(int\\)' throws an exception\.\.\." "" { target *-*-* } .-1 }
+
+ return 0;
+}
+
+int outer (int x)
+{
+ void *ptr = __builtin_malloc (1024); // { dg-message "allocated here" }
+
+ int rval = inner (x);
+
+ __builtin_free (ptr);
+
+ return rval;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-nothrow.C b/gcc/testsuite/g++.dg/analyzer/exception-nothrow.C
new file mode 100644
index 0000000..9625748
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-nothrow.C
@@ -0,0 +1,26 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+extern void do_something () __attribute__((nothrow));
+
+/* A wrapper function to stop the try/catch being optimized away. */
+
+void wrapper () __attribute__((noinline));
+void wrapper ()
+{
+ do_something ();
+}
+
+int test ()
+{
+ try
+ {
+ wrapper ();
+ }
+ catch (int i)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-1.C b/gcc/testsuite/g++.dg/analyzer/exception-path-1.C
new file mode 100644
index 0000000..486ca193
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-1.C
@@ -0,0 +1,34 @@
+/* Verify that we follow the correct paths when we know the typeinfo of
+ an exception. */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+int test ()
+{
+ try
+ {
+ throw value_error (); // { dg-message "\\(1\\) throwing exception of type 'value_error' here..." }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (const io_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ catch (const value_error &err) // { dg-message "\\(2\\) \.\.\.catching exception of type 'value_error' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 2;
+ }
+ catch (const runtime_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 3;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-1.C b/gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-1.C
new file mode 100644
index 0000000..8aa9814
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-1.C
@@ -0,0 +1,16 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ try
+ {
+ throw 42;
+ }
+ catch (...)
+ {
+ __analyzer_dump_path ();
+ return -1;
+ }
+ __analyzer_dump_path ();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-2.C b/gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-2.C
new file mode 100644
index 0000000..d185056
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-catch-all-2.C
@@ -0,0 +1,22 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ try
+ {
+ throw 42; // { dg-message "throwing exception of type 'int' here\.\.\." }
+ }
+ catch (int i) // { dg-message "\.\.\.catching exception of type 'int' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ __analyzer_eval (i == 42); // { dg-warning "TRUE" }
+ return -2;
+ }
+ catch (...)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return -1;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C
new file mode 100644
index 0000000..2608f17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C
@@ -0,0 +1,55 @@
+/* Verify that we follow the correct paths when we know the typeinfo of
+ an exception: interprocedural case where unwind multiple frame,
+ failing to match the type. */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+int inner (int flag)
+{
+ if (flag)
+ throw value_error (); // { dg-message "throwing exception of type 'value_error' here..." }
+ // { dg-message "unwinding 2 stack frames" "" { target *-*-* } .-1 }
+
+ return 0;
+}
+
+int middle (int flag)
+{
+ try
+ {
+ return inner (flag);
+ }
+ catch (const io_error &err) // this shouldn't be matched
+ {
+ return -1;
+ }
+}
+
+int outer ()
+{
+ try
+ {
+ middle (1);
+ }
+ catch (const io_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ catch (const value_error &err) // { dg-message "\.\.\.catching exception of type 'value_error' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 2;
+ }
+ catch (const runtime_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 3;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple.C b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple.C
new file mode 100644
index 0000000..a52312a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple.C
@@ -0,0 +1,48 @@
+/* Verify that we follow the correct paths when we know the typeinfo of
+ an exception: interprocedural case where we unwind one frame. */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+int inner (int flag)
+{
+ if (flag)
+ throw value_error (); // { dg-message "throwing exception of type 'value_error' here..." }
+
+ return 0;
+}
+
+int middle (int flag)
+{
+ return inner (flag);
+}
+
+int outer ()
+{
+ try
+ {
+ middle (1);
+ }
+ catch (const io_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ catch (const value_error &err) // { dg-message "\.\.\.catching exception of type 'value_error' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 2;
+ }
+ catch (const runtime_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 3;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
+
+// TODO: test coverage for unwinding stack frame events
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-single.C b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-single.C
new file mode 100644
index 0000000..055fe27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-single.C
@@ -0,0 +1,43 @@
+/* Verify that we follow the correct paths when we know the typeinfo of
+ an exception: interprocedural case where we unwind two frames. */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+int inner (int flag)
+{
+ if (flag)
+ throw value_error (); // { dg-message "throwing exception of type 'value_error' here..." }
+
+ return 0;
+}
+
+int outer ()
+{
+ try
+ {
+ inner (1);
+ }
+ catch (const io_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 1;
+ }
+ catch (const value_error &err) // { dg-message "\.\.\.catching exception of type 'value_error' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 2;
+ }
+ catch (const runtime_error &err)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 3;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
+
+// TODO: test coverage for unwinding stack frame events
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-with-cleanups.C b/gcc/testsuite/g++.dg/analyzer/exception-path-with-cleanups.C
new file mode 100644
index 0000000..fc18a21
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-path-with-cleanups.C
@@ -0,0 +1,27 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct foo
+{
+ foo (int x) : m_x (x) {}
+ ~foo () __attribute__((nothrow));
+
+ int m_x;
+};
+
+int test (bool flag)
+{
+ foo outside (1);
+ try
+ {
+ foo inside_try (2);
+ if (flag)
+ throw foo (3); // { dg-message "throwing exception of type 'foo' here\.\.\." }
+ }
+ catch (foo &f) // { dg-message "\.\.\.catching exception of type 'foo' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ __analyzer_eval (f.m_x == 3); // { dg-warning "TRUE" }
+ return f.m_x;
+ }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-rethrow-1.C b/gcc/testsuite/g++.dg/analyzer/exception-rethrow-1.C
new file mode 100644
index 0000000..10484fd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-rethrow-1.C
@@ -0,0 +1,13 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ try
+ {
+ throw 42;
+ }
+ catch (...)
+ {
+ throw;
+ }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-rethrow-2.C b/gcc/testsuite/g++.dg/analyzer/exception-rethrow-2.C
new file mode 100644
index 0000000..22242a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-rethrow-2.C
@@ -0,0 +1,25 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ try
+ {
+ try
+ {
+ throw 42; // { dg-message "\\(1\\) throwing exception of type 'int' here\.\.\." }
+ }
+ catch (...) // { dg-message "\\(2\\) \.\.\.catching exception of type 'int' here" }
+ {
+ throw; // { dg-message "\\(3\\) rethrowing exception of type 'int' here\.\.\." }
+ }
+ }
+ catch (int i) // { dg-message "\\(4\\) \.\.\.catching exception of type 'int' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ __analyzer_eval (i == 42); // { dg-warning "TRUE" }
+ return -1;
+ }
+
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-stack-1.C b/gcc/testsuite/g++.dg/analyzer/exception-stack-1.C
new file mode 100644
index 0000000..c6aa415
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-stack-1.C
@@ -0,0 +1,35 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+void test (void)
+{
+ try
+ {
+ try
+ {
+ throw 42;
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (...)
+ {
+ try
+ {
+ throw 1066; // throw an inner exception
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (int i)
+ {
+ __analyzer_eval (i == 1066); // { dg-warning "TRUE" }
+ }
+ throw; // rethrow the outer exception
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (int j)
+ {
+ __analyzer_eval (j == 42); // { dg-warning "TRUE" }
+ __analyzer_dump_path (); // { dg-message "path" }
+ throw;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-stack-2.C b/gcc/testsuite/g++.dg/analyzer/exception-stack-2.C
new file mode 100644
index 0000000..5e6a3c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-stack-2.C
@@ -0,0 +1,44 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct io_error {};
+struct value_error {};
+struct runtime_error {};
+
+void test (void)
+{
+ try
+ {
+ try
+ {
+ throw value_error (); // { dg-message "\\(1\\) throwing exception of type 'value_error' here..." }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (...) // { dg-message "\\(2\\) \.\.\.catching exception of type 'value_error' here" }
+ {
+ try
+ {
+ throw io_error (); // { dg-message "\\(3\\) throwing exception of type 'io_error' here..." }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (const io_error &err) // { dg-message "\\(4\\) \.\.\.catching exception of type 'io_error' here" }
+ {
+ /* discard it */
+ }
+
+ // rethrow the outer exception
+ throw; // { dg-message "\\(5\\) rethrowing exception of type 'value_error' here..." }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (const value_error &err) // { dg-message "\\(6\\) \.\.\.catching exception of type 'value_error' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ throw;
+ }
+ catch (...)
+ {
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C b/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
new file mode 100644
index 0000000..79df330
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-1.C
@@ -0,0 +1,21 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+class exception
+{
+};
+
+class io_error : public exception
+{
+};
+
+int test ()
+{
+ try {
+ throw io_error();
+ } catch (exception &exc) {
+ __analyzer_dump_path (); // { dg-message "path" "PR analyzer/119697" { xfail *-*-* } }
+ return -1;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C b/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C
new file mode 100644
index 0000000..e9fb617
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-subclass-2.C
@@ -0,0 +1,25 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+class exception
+{
+};
+
+class io_error : public exception
+{
+};
+
+int __analyzer_inner ()
+{
+ try {
+ throw io_error();
+ } catch (exception &exc) {
+ return -1;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
+
+int test ()
+{
+ return __analyzer_inner (); // { dg-message "path" "PR analyzer/119697" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-value-1.C b/gcc/testsuite/g++.dg/analyzer/exception-value-1.C
new file mode 100644
index 0000000..0d06dd8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-value-1.C
@@ -0,0 +1,20 @@
+/* Verify that we can access values in exceptions. */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+int test ()
+{
+ try
+ {
+ throw 42; // { dg-message "\\(1\\) throwing exception of type 'int' here..." }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ }
+ catch (int i) // { dg-message "\\(2\\) \.\.\.catching exception of type 'int' here" }
+ {
+ __analyzer_dump_path (); // { dg-message "path" }
+ __analyzer_eval (i == 42); // { dg-warning "TRUE" }
+ return 1;
+ }
+ __analyzer_dump_path (); // { dg-bogus "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/exception-value-2.C b/gcc/testsuite/g++.dg/analyzer/exception-value-2.C
new file mode 100644
index 0000000..ef9dd46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/exception-value-2.C
@@ -0,0 +1,36 @@
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+struct foo {};
+
+int inner (bool flag)
+{
+ try
+ {
+ if (flag)
+ throw 42;
+ }
+ catch (foo &f)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+int middle (bool flag)
+{
+ try
+ {
+ int rval = inner (flag);
+ return rval;
+ }
+ catch (int ei)
+ {
+ return ei;
+ }
+}
+
+void outer (void)
+{
+ __analyzer_eval (middle (false) == 0); // { dg-warning "TRUE" }
+ __analyzer_eval (middle (true) == 42); // { dg-warning "TRUE" }
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/fno-exception.C b/gcc/testsuite/g++.dg/analyzer/fno-exception.C
new file mode 100644
index 0000000..2ec4e06e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fno-exception.C
@@ -0,0 +1,12 @@
+/* { dg-additional-options "-fno-exceptions" } */
+
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+extern void do_something ();
+
+int test ()
+{
+ do_something ();
+ __analyzer_dump_path (); // { dg-message "path" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/analyzer/pr94028.C b/gcc/testsuite/g++.dg/analyzer/pr94028.C
index 0573d30..53bfa29 100644
--- a/gcc/testsuite/g++.dg/analyzer/pr94028.C
+++ b/gcc/testsuite/g++.dg/analyzer/pr94028.C
@@ -19,7 +19,7 @@ struct j
throw()
#endif
{
- return calloc (b, sizeof (int)); // { dg-bogus "leak" "" { xfail c++98_only } }
+ return calloc (b, sizeof (int));
}
j (B *, int)
{
diff --git a/gcc/testsuite/g++.dg/analyzer/std-unexpected.C b/gcc/testsuite/g++.dg/analyzer/std-unexpected.C
new file mode 100644
index 0000000..4eb2672
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/std-unexpected.C
@@ -0,0 +1,9 @@
+// { dg-require-effective-target c++14_down }
+// { dg-additional-options "-Wno-deprecated-declarations" }
+
+#include <exception>
+
+void test_1 ()
+{
+ std::unexpected ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105287.C b/gcc/testsuite/g++.dg/coroutines/pr105287.C
index c54d1fd..0436572 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr105287.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr105287.C
@@ -1,5 +1,5 @@
// { dg-additional-options "-fanalyzer" }
-// { dg-excess-errors "lots of analyzer output, but no ICE" }
+
namespace std {
template <typename _Result> struct coroutine_traits : _Result {};
template <typename = void> struct coroutine_handle {