Implement -Winfinite-recursion [PR88232].
Resolves:
PR middle-end/88232 - Please implement -Winfinite-recursion
gcc/ChangeLog:
PR middle-end/88232
* Makefile.in (OBJS): Add gimple-warn-recursion.o.
* common.opt: Add -Winfinite-recursion.
* doc/invoke.texi (-Winfinite-recursion): Document.
* passes.def (pass_warn_recursion): Schedule a new pass.
* tree-pass.h (make_pass_warn_recursion): Declare.
* gimple-warn-recursion.c: New file.
gcc/c-family/ChangeLog:
PR middle-end/88232
* c.opt: Add -Winfinite-recursion.
gcc/testsuite/ChangeLog:
PR middle-end/88232
* c-c++-common/attr-used-5.c: Suppress valid warning.
* c-c++-common/attr-used-6.c: Same.
* c-c++-common/attr-used-9.c: Same.
* g++.dg/warn/Winfinite-recursion-2.C: New test.
* g++.dg/warn/Winfinite-recursion-3.C: New test.
* g++.dg/warn/Winfinite-recursion.C: New test.
* gcc.dg/Winfinite-recursion-2.c: New test.
* gcc.dg/Winfinite-recursion.c: New test.
@@ -1420,6 +1420,7 @@ OBJS = \
gimple-streamer-in.o \
gimple-streamer-out.o \
gimple-walk.o \
+ gimple-warn-recursion.o \
gimplify.o \
gimplify-me.o \
godump.o \
@@ -714,6 +714,10 @@ Wincompatible-pointer-types
C ObjC Var(warn_incompatible_pointer_types) Init(1) Warning
Warn when there is a conversion between pointers that have incompatible types.
+Winfinite-recursion
+C ObjC C++ LTO ObjC++ Var(warn_infinite_recursion) Warning LangEnabledBy(C ObjC C++ LTO ObjC++, Wall)
+Warn for infinitely recursive calls.
+
Waddress-of-packed-member
C ObjC C++ ObjC++ Var(warn_address_of_packed_member) Init(1) Warning
Warn when the address of packed member of struct or union is taken.
@@ -636,6 +636,10 @@ Wimplicit-fallthrough=
Common Var(warn_implicit_fallthrough) RejectNegative Joined UInteger Warning IntegerRange(0, 5)
Warn when a switch case falls through.
+Winfinite-recursion
+Var(warn_infinite_recursion) Warning
+Warn for infinitely recursive calls.
+
Winline
Common Var(warn_inline) Warning Optimization
Warn when an inlined function cannot be inlined.
@@ -357,6 +357,7 @@ Objective-C and Objective-C++ Dialects}.
-Wignored-qualifiers -Wno-incompatible-pointer-types @gol
-Wimplicit -Wimplicit-fallthrough -Wimplicit-fallthrough=@var{n} @gol
-Wno-implicit-function-declaration -Wno-implicit-int @gol
+-Winfinite-recursion @gol
-Winit-self -Winline -Wno-int-conversion -Wint-in-bool-context @gol
-Wno-int-to-pointer-cast -Wno-invalid-memory-model @gol
-Winvalid-pch -Wjump-misses-init -Wlarger-than=@var{byte-size} @gol
@@ -6179,6 +6180,14 @@ is only active when @option{-fdelete-null-pointer-checks} is active,
which is enabled by optimizations in most targets. The precision of
the warnings depends on the optimization options used.
+@item -Winfinite-recursion
+@opindex Winfinite-recursion
+@opindex Wno-infinite-recursion
+Warn about infinitely recursive calls. The warning is effective at all
+optimization levels but requires optimization in order to detect infinite
+recursion in calls between two or more functions.
+@option{-Winfinite-recursion} is included in @option{-Wall}.
+
@item -Winit-self @r{(C, C++, Objective-C and Objective-C++ only)}
@opindex Winit-self
@opindex Wno-init-self
new file mode 100644
@@ -0,0 +1,202 @@
+/* -Winfinite-recursion support.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ Contributed by Martin Sebor <msebor@redhat.com>
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "diagnostic-core.h"
+// #include "tree-dfa.h"
+#include "attribs.h"
+#include "gimple-iterator.h"
+
+namespace {
+
+const pass_data warn_recursion_data =
+{
+ GIMPLE_PASS, /* type */
+ "*infinite-recursion", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_warn_recursion : public gimple_opt_pass
+{
+public:
+ pass_warn_recursion (gcc::context *);
+
+private:
+ virtual bool gate (function *) { return warn_infinite_recursion; }
+
+ virtual unsigned int execute (function *);
+
+ bool find_function_exit (basic_block);
+
+ /* Recursive calls found in M_FUNC. */
+ vec<gimple *> *m_calls;
+ /* Basic blocks already visited in the current function. */
+ bitmap m_visited;
+ /* The current function. */
+ function *m_func;
+ /* The current function code if it's (also) a built-in. */
+ built_in_function m_built_in;
+ /* True if M_FUNC is a noreturn function. */
+ bool noreturn_p;
+};
+
+/* Initialize the pass and its members. */
+
+pass_warn_recursion::pass_warn_recursion (gcc::context *ctxt)
+ : gimple_opt_pass (warn_recursion_data, ctxt),
+ m_calls (), m_visited (), m_func (), m_built_in (), noreturn_p ()
+{
+}
+
+/* Return true if there is path from BB to M_FUNC exit point along which
+ there is no (recursive) call to M_FUNC. */
+
+bool
+pass_warn_recursion::find_function_exit (basic_block bb)
+{
+ if (!bitmap_set_bit (m_visited, bb->index))
+ return false;
+
+ if (bb == EXIT_BLOCK_PTR_FOR_FN (m_func))
+ return true;
+
+ /* Iterate over statements in BB, looking for a call to FNDECL. */
+ for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next_nondebug (&si))
+ {
+ gimple *stmt = gsi_stmt (si);
+ if (!is_gimple_call (stmt))
+ continue;
+
+ if (gimple_call_builtin_p (stmt, BUILT_IN_LONGJMP))
+ /* A longjmp breaks infinite recursion. */
+ return true;
+
+ if (tree fndecl = gimple_call_fndecl (stmt))
+ {
+ /* A throw statement breaks infinite recursion. */
+ tree id = DECL_NAME (fndecl);
+ const char *name = IDENTIFIER_POINTER (id);
+ if (startswith (name, "__cxa_throw"))
+ return true;
+ /* As does a call to POSIX siglongjmp. */
+ if (!strcmp (name, "siglongjmp"))
+ return true;
+
+ if (m_built_in && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+ && m_built_in == DECL_FUNCTION_CODE (fndecl))
+ {
+ /* The call is being made from the definition of a built-in
+ (e.g., in a replacement of one) to itself. */
+ m_calls->safe_push (stmt);
+ return false;
+ }
+ }
+
+ if (noreturn_p)
+ {
+ /* A noreturn call breaks infinite recursion. */
+ int flags = gimple_call_flags (stmt);
+ if (flags & ECF_NORETURN)
+ return true;
+ }
+
+ tree callee = gimple_call_fndecl (stmt);
+ if (!callee || m_func->decl != callee)
+ continue;
+
+ /* Add the recursive call to the vector and return false. */
+ m_calls->safe_push (stmt);
+ return false;
+ }
+
+ /* If no call to FNDECL has been found search all BB's successors. */
+ edge e;
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (find_function_exit (e->dest))
+ return true;
+
+ return false;
+}
+
+
+/* Search FUNC for unconditionally infinitely recursive calls to self
+ and issue a warning if it is such a function. */
+
+unsigned int
+pass_warn_recursion::execute (function *func)
+{
+ auto_bitmap visited;
+ auto_vec<gimple *> calls;
+
+ m_visited = visited;
+ m_calls = &calls;
+ m_func = func;
+
+ /* Avoid diagnosing an apparently infinitely recursive function that
+ doesn't return where the infinite recursion might be avoided by
+ a call to another noreturn function. */
+ noreturn_p = lookup_attribute ("noreturn", DECL_ATTRIBUTES (m_func->decl));
+
+ if (fndecl_built_in_p (m_func->decl, BUILT_IN_NORMAL))
+ m_built_in = DECL_FUNCTION_CODE (m_func->decl);
+ else
+ m_built_in = BUILT_IN_NONE;
+
+ basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (func);
+
+ if (find_function_exit (entry_bb) || m_calls->length () == 0)
+ return 0;
+
+ if (warning_at (DECL_SOURCE_LOCATION (func->decl),
+ OPT_Winfinite_recursion,
+ "infinite recursion detected"))
+ for (auto stmt: *m_calls)
+ {
+ location_t loc = gimple_location (stmt);
+ if (loc == UNKNOWN_LOCATION)
+ continue;
+
+ inform (loc, "recursive call");
+ }
+
+ return 0;
+}
+
+} // namespace
+
+gimple_opt_pass *
+make_pass_warn_recursion (gcc::context *ctxt)
+{
+ return new pass_warn_recursion (ctxt);
+}
@@ -71,6 +71,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_rebuild_cgraph_edges);
NEXT_PASS (pass_local_fn_summary);
NEXT_PASS (pass_early_inline);
+ NEXT_PASS (pass_warn_recursion);
NEXT_PASS (pass_all_early_optimizations);
PUSH_INSERT_PASSES_WITHIN (pass_all_early_optimizations)
NEXT_PASS (pass_remove_cgraph_callee_edges);
@@ -1,6 +1,6 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
-/* { dg-options "-Wall -O2" } */
+/* { dg-options "-Wall -Wno-infinite-recursion -O2" } */
struct dtv_slotinfo_list
{
@@ -1,6 +1,6 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
-/* { dg-options "-Wall -O2" } */
+/* { dg-options "-Wall -Wno-infinite-recursion -O2" } */
struct dtv_slotinfo_list
{
@@ -1,6 +1,6 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
-/* { dg-options "-Wall -O2" } */
+/* { dg-options "-Wall -Wno-infinite-recursion -O2" } */
struct dtv_slotinfo_list
{
new file mode 100644
@@ -0,0 +1,75 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ Test case from PR 87742 (see PR 88232, comment 2.
+ { dg-do compile { target c++11 } }
+ { dg-options "-Wall -Winfinite-recursion" } */
+
+namespace std
+{
+class type_info {
+public:
+ void k() const;
+};
+
+} // namespace std
+
+using std::type_info;
+
+template <int a> struct f { static constexpr int c = a; };
+struct h {
+ typedef int e;
+};
+
+template <unsigned long, typename...> struct m;
+template <unsigned long ab, typename i, typename j, typename... ac>
+struct m<ab, i, j, ac...> : m<ab + 1, i, ac...> {};
+template <unsigned long ab, typename j, typename... ac>
+struct m<ab, j, j, ac...> : f<ab> {};
+template <unsigned long, typename...> struct n;
+template <unsigned long ab, typename j, typename... ac>
+struct n<ab, j, ac...> : n<ab - 1, ac...> {};
+template <typename j, typename... ac> struct n<0, j, ac...> : h {};
+template <typename... l> class F {
+ template <typename i> struct I : m<0, i, l...> {};
+ template <int ab> struct s : n<ab, l...> {};
+ static const type_info *const b[];
+ struct G {
+ template <typename ag>
+ operator ag() const // { dg-warning "-Winfinite-recursion" }
+ {
+ return *this;
+ }
+ };
+ unsigned o;
+ G ah;
+
+public:
+ F();
+ long t() const { return o; }
+ const type_info &m_fn3() const { return *b[o]; }
+ template <int ab> typename s<ab>::e *m_fn4() const {
+ if (o != ab)
+ return nullptr;
+ return ah;
+ }
+ template <int ab> void m_fn5() const {
+ m_fn4<ab>();
+ const type_info &r = m_fn3();
+ r.k();
+ }
+ template <typename i> void u() const { m_fn5<I<i>::c>(); }
+};
+template <typename... l> const type_info *const F<l...>::b[] {&typeid(l)...};
+using am = unsigned char;
+class H {
+ enum bd : am { be = 2 };
+ using bf = F<int, int, H>;
+ bf ah;
+ template <typename bg> void v() const { ah.u<bg>(); }
+ void w() const;
+};
+void H::w() const {
+ bd d = bd(ah.t());
+ switch (d)
+ case be:
+ v<H>();
+}
new file mode 100644
@@ -0,0 +1,75 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ { dg-do compile }
+ { dg-options "-Wall -Winfinite-recursion" } */
+
+/* Might throw. */
+void f ();
+
+/* Verify a warning is issued even though a call to f() might throw,
+ breaking the infinite recursion. */
+
+void warn_f_call_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ if (n > 7)
+ f ();
+ warn_f_call_r (n - 1); // { dg-message "recursive call" }
+}
+
+void warn_f_do_while_call_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ f ();
+ do
+ {
+ f ();
+ warn_f_do_while_call_r (n - 1); // { dg-message "recursive call" }
+ }
+ while (1);
+}
+
+
+struct X
+{
+ X (int);
+ ~X ();
+};
+
+/* Verify a warning even though the X ctor might throw, breaking
+ the recursion. Using possible throwing to suppress the warning
+ would make it pretty much useless in C++. */
+
+int warn_class_with_ctor (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ X x (n);
+ return n + warn_class_with_ctor (n - 1);
+}
+
+
+int nowarn_throw (int n)
+{
+ if (n > 7)
+ throw "argument too big";
+
+ return n + nowarn_throw (n - 1);
+}
+
+
+/* Verify call operator new doesn't suppress the warning even though
+ it might throw. */
+
+extern int* eipa[];
+
+void warn_call_new (int i) // { dg-warning "-Winfinite-recursion" }
+{
+ eipa[i] = new int;
+
+ warn_call_new (i - 1);
+}
+
+/* Verify a recursive call to operator new. */
+
+void* operator new[] (size_t n) // { dg-warning "-Winfinite-recursion" }
+{
+ char *p = new char[n + sizeof (n)]; // { dg-message "recursive call" }
+ *(size_t*)p = n;
+ return p + sizeof n;
+}
new file mode 100644
@@ -0,0 +1,34 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ { dg-do compile }
+ { dg-options "-Wall -Winfinite-recursion" } */
+
+template <typename D>
+struct C
+{
+ void foo () // { dg-warning "-Winfinite-recursion" }
+ {
+ static_cast<D *>(this)->foo ();
+ }
+};
+
+struct D : C<D>
+{
+ // this is missing:
+ // void foo() {}
+};
+
+void f (D *d)
+{
+ d->foo ();
+}
+
+
+struct E : C<D>
+{
+ void foo() {}
+};
+
+void g (E *e)
+{
+ e->foo ();
+}
new file mode 100644
@@ -0,0 +1,252 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ Exercise warning with optimization. Same as -Winfinite-recursion.c
+ plus mutually recursive calls that depend on inlining.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Winfinite-recursion" } */
+
+#define NORETURN __attribute__ ((noreturn))
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void abort (void);
+extern void exit (int);
+
+extern int ei;
+int (*pfi_v)(void);
+
+
+/* Make sure the warning doesn't assume every call has a DECL. */
+
+int nowarn_pfi_v (void)
+{
+ return pfi_v ();
+}
+
+
+int warn_fi_v (void) // { dg-warning "-Winfinite-recursion" }
+{
+ return warn_fi_v (); // { dg-message "recursive call" }
+}
+
+/* Verify #pragma suppression works. */
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winfinite-recursion"
+
+int suppress_warn_fi_v (void)
+{
+ return warn_fi_v ();
+}
+
+#pragma GCC diagnostic pop
+
+int nowarn_fi_v (void)
+{
+ if (ei++ == 0)
+ return nowarn_fi_v ();
+ return 0;
+}
+
+
+int warn_if_i (int i) // { dg-warning "-Winfinite-recursion" }
+{
+ if (i > 0)
+ return warn_if_i (--i); // { dg-message "recursive call" }
+ else if (i < 0)
+ return warn_if_i (-i); // { dg-message "recursive call" }
+ else
+ return warn_if_i (7); // { dg-message "recursive call" }
+}
+
+
+int nowarn_if_i (int i)
+{
+ if (i > 0)
+ return nowarn_if_i (--i);
+ else if (i < 0)
+ return nowarn_if_i (-i);
+ else
+ return -1;
+}
+
+int nowarn_switch (int i, int a[])
+{
+ switch (i)
+ {
+ case 0: return nowarn_switch (a[3], a + 1);
+ case 1: return nowarn_switch (a[5], a + 2);
+ case 2: return nowarn_switch (a[7], a + 3);
+ case 3: return nowarn_switch (a[9], a + 4);
+ }
+ return 77;
+}
+
+int warn_switch (int i, int a[]) // { dg-warning "-Winfinite-recursion" }
+{
+ switch (i)
+ {
+ case 0: return warn_switch (a[3], a + 1);
+ case 1: return warn_switch (a[5], a + 2);
+ case 2: return warn_switch (a[7], a + 3);
+ case 3: return warn_switch (a[9], a + 4);
+ default: return warn_switch (a[1], a + 5);
+ }
+}
+
+NORETURN void fnoreturn (void);
+
+/* Verify there's no warning for a function that doesn't return. */
+int nowarn_call_noret (void)
+{
+ fnoreturn ();
+}
+
+int warn_call_noret_r (void) // { dg-warning "-Winfinite-recursion" }
+{
+ warn_call_noret_r (); // { dg-message "recursive call" }
+ fnoreturn ();
+}
+
+/* Verify a warning even though the abort() call would prevent the infinite
+ recursion. There's no good way to tell the two cases apart and letting
+ a simple abort prevent the warning would make it ineffective in cases
+ where it's the result of assert() expansion and not meant to actually
+ prevent recursion. */
+
+int
+warn_noret_call_abort_r (char *s, int n) // { dg-warning "-Winfinite-recursion" }
+{
+ if (!s)
+ abort ();
+
+ if (n > 7)
+ abort ();
+
+ return n + warn_noret_call_abort_r (s, n - 1); // { dg-message "recursive call" }
+}
+
+/* Verify that a warning is not issued for an apparently infinitely
+ recursive function like the one above where the recursion would be
+ prevented by a call to a noreturn function if the recursive function
+ is itself declared noreturn. */
+
+NORETURN void nowarn_noret_call_abort_r (int n)
+{
+ if (n > 7)
+ abort ();
+
+ nowarn_noret_call_abort_r (n - 1);
+}
+
+int warn_call_abort_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ n += warn_call_abort_r (n - 1); // { dg-message "recursive call" }
+ if (n > 7) // unreachable
+ abort ();
+ return n;
+}
+
+
+/* And again with exit() for good measure. */
+
+int warn_call_exit_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ n += warn_call_exit_r (n - 1); // { dg-message "recursive call" }
+ if (n > 7)
+ exit (0);
+ return n;
+}
+
+struct __jmp_buf_tag { };
+typedef struct __jmp_buf_tag jmp_buf[1];
+
+extern jmp_buf jmpbuf;
+
+/* A call to longjmp() breaks infinite recursion. Verify it suppresses
+ the warning. */
+
+int nowarn_call_longjmp_r (int n)
+{
+ if (n > 7)
+ __builtin_longjmp (jmpbuf, 1);
+ return n + nowarn_call_longjmp_r (n - 1);
+}
+
+int warn_call_longjmp_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ n += warn_call_longjmp_r (n - 1); // { dg-message "recursive call" }
+ if (n > 7)
+ __builtin_longjmp (jmpbuf, 1);
+ return n;
+}
+
+
+struct __sigjmp_buf_tag { };
+typedef struct __sigjmp_buf_tag sigjmp_buf[1];
+
+extern sigjmp_buf sigjmpbuf;
+
+/* GCC has no __builtin_siglongjmp(). */
+extern void siglongjmp (sigjmp_buf, int);
+
+/* A call to longjmp() breaks infinite recursion. Verify it suppresses
+ the warning. */
+
+int nowarn_call_siglongjmp_r (int n)
+{
+ if (n > 7)
+ siglongjmp (sigjmpbuf, 1);
+ return n + nowarn_call_siglongjmp_r (n - 1);
+}
+
+
+int nowarn_while_do_call_r (int n)
+{
+ int z = 0;
+ while (n)
+ z += nowarn_while_do_call_r (n--);
+ return z;
+}
+
+int warn_do_while_call_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ int z = 0;
+ do
+ z += warn_do_while_call_r (n); // { dg-message "recursive call" }
+ while (--n);
+ return z;
+}
+
+
+/* Verify warnings for a naive replacement of a built-in fucntion. */
+
+void* malloc (size_t n) // { dg-warning "-Winfinite-recursion" }
+{
+ size_t *p =
+ (size_t*)__builtin_malloc (n + sizeof n); // { dg-message "recursive call" }
+ *p = n;
+ return p + 1;
+}
+
+
+int nowarn_fact (int n)
+{
+ return n ? n * nowarn_fact (n - 1) : 1;
+}
+
+
+static int fi_v (void);
+
+/* It would seem preferable to issue the warning for the extern function
+ but as it happens it's the static function that's inlined into a recursive
+ call to itself and warn_call_fi_v() expands to a call to it. */
+
+int warn_call_fi_v (void) // { dg-warning "-Winfinite-recursion" "" { xfail *-*-* } }
+{
+ return fi_v (); // { dg-message "recursive call" }
+}
+
+static int fi_v (void) // { dg-warning "-Winfinite-recursion" }
+{
+ return warn_call_fi_v ();
+}
new file mode 100644
@@ -0,0 +1,227 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ Verify simple cases without optimization.
+ { dg-do compile }
+ { dg-options "-Wall -Winfinite-recursion" } */
+
+#define NORETURN __attribute__ ((noreturn))
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void abort (void);
+extern void exit (int);
+
+extern int ei;
+int (*pfi_v)(void);
+
+
+/* Make sure the warning doesn't assume every call has a DECL. */
+
+int nowarn_pfi_v (void)
+{
+ return pfi_v ();
+}
+
+
+int warn_fi_v (void) // { dg-warning "-Winfinite-recursion" }
+{
+ return warn_fi_v (); // { dg-message "recursive call" }
+}
+
+/* Verify #pragma suppression works. */
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winfinite-recursion"
+
+int suppress_warn_fi_v (void)
+{
+ return warn_fi_v ();
+}
+
+#pragma GCC diagnostic pop
+
+
+int nowarn_fi_v (void)
+{
+ if (ei++ == 0)
+ return nowarn_fi_v ();
+ return 0;
+}
+
+int warn_if_i (int i) // { dg-warning "-Winfinite-recursion" }
+{
+ if (i > 0)
+ return warn_if_i (--i); // { dg-message "recursive call" }
+ else if (i < 0)
+ return warn_if_i (-i); // { dg-message "recursive call" }
+ else
+ return warn_if_i (7); // { dg-message "recursive call" }
+}
+
+
+int nowarn_if_i (int i)
+{
+ if (i > 0)
+ return nowarn_if_i (--i);
+ else if (i < 0)
+ return nowarn_if_i (-i);
+ else
+ return -1;
+}
+
+int nowarn_switch (int i, int a[])
+{
+ switch (i)
+ {
+ case 0: return nowarn_switch (a[3], a + 1);
+ case 1: return nowarn_switch (a[5], a + 2);
+ case 2: return nowarn_switch (a[7], a + 3);
+ case 3: return nowarn_switch (a[9], a + 4);
+ }
+ return 77;
+}
+
+int warn_switch (int i, int a[]) // { dg-warning "-Winfinite-recursion" }
+{
+ switch (i)
+ {
+ case 0: return warn_switch (a[3], a + 1);
+ case 1: return warn_switch (a[5], a + 2);
+ case 2: return warn_switch (a[7], a + 3);
+ case 3: return warn_switch (a[9], a + 4);
+ default: return warn_switch (a[1], a + 5);
+ }
+}
+
+NORETURN void fnoreturn (void);
+
+/* Verify there's no warning for a function that doesn't return. */
+int nowarn_call_noret (void)
+{
+ fnoreturn ();
+}
+
+int warn_call_noret_r (void) // { dg-warning "-Winfinite-recursion" }
+{
+ warn_call_noret_r (); // { dg-message "recursive call" }
+ fnoreturn ();
+}
+
+/* Verify a warning even though the abort() call would prevent the infinite
+ recursion. There's no good way to tell the two cases apart and letting
+ a simple abort prevent the warning would make it ineffective in cases
+ where it's the result of assert() expansion and not meant to actually
+ prevent recursion. */
+
+int
+warn_noret_call_abort_r (char *s, int n) // { dg-warning "-Winfinite-recursion" }
+{
+ if (!s)
+ abort ();
+
+ if (n > 7)
+ abort ();
+
+ return n + warn_noret_call_abort_r (s, n - 1); // { dg-message "recursive call" }
+}
+
+/* Verify that a warning is not issued for an apparently infinitely
+ recursive function like the one above where the recursion would be
+ prevented by a call to a noreturn function if the recursive function
+ is itself declared noreturn. */
+
+NORETURN void nowarn_noret_call_abort_r (int n)
+{
+ if (n > 7)
+ abort ();
+
+ nowarn_noret_call_abort_r (n - 1);
+}
+
+int warn_call_abort_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ n += warn_call_abort_r (n - 1); // { dg-message "recursive call" }
+ if (n > 7) // unreachable
+ abort ();
+ return n;
+}
+
+
+/* And again with exit() for good measure. */
+
+int warn_call_exit_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ n += warn_call_exit_r (n - 1); // { dg-message "recursive call" }
+ if (n > 7)
+ exit (0);
+ return n;
+}
+
+struct __jmp_buf_tag { };
+typedef struct __jmp_buf_tag jmp_buf[1];
+
+extern jmp_buf jmpbuf;
+
+/* A call to longjmp() breaks infinite recursion. Verify it suppresses
+ the warning. */
+
+int nowarn_call_longjmp_r (int n)
+{
+ if (n > 7)
+ __builtin_longjmp (jmpbuf, 1);
+ return n + nowarn_call_longjmp_r (n - 1);
+}
+
+int warn_call_longjmp_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ n += warn_call_longjmp_r (n - 1); // { dg-message "recursive call" }
+ if (n > 7)
+ __builtin_longjmp (jmpbuf, 1);
+ return n;
+}
+
+
+struct __sigjmp_buf_tag { };
+typedef struct __sigjmp_buf_tag sigjmp_buf[1];
+
+extern sigjmp_buf sigjmpbuf;
+
+/* GCC has no __builtin_siglongjmp(). */
+extern void siglongjmp (sigjmp_buf, int);
+
+/* A call to longjmp() breaks infinite recursion. Verify it suppresses
+ the warning. */
+
+int nowarn_call_siglongjmp_r (int n)
+{
+ if (n > 7)
+ siglongjmp (sigjmpbuf, 1);
+ return n + nowarn_call_siglongjmp_r (n - 1);
+}
+
+
+int nowarn_while_do_call_r (int n)
+{
+ int z = 0;
+ while (n)
+ z += nowarn_while_do_call_r (n--);
+ return z;
+}
+
+int warn_do_while_call_r (int n) // { dg-warning "-Winfinite-recursion" }
+{
+ int z = 0;
+ do
+ z += warn_do_while_call_r (n); // { dg-message "recursive call" }
+ while (--n);
+ return z;
+}
+
+/* Verify warnings for a naive replacement of a built-in fucntion. */
+
+void* malloc (size_t n) // { dg-warning "-Winfinite-recursion" }
+{
+ size_t *p =
+ (size_t*)__builtin_malloc (n + sizeof n); // { dg-message "recursive call" }
+ *p = n;
+ return p + 1;
+}
@@ -435,6 +435,7 @@ extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_early_object_sizes (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_access (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_recursion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt);