implement -Winfinite-recursion [PR88232]
Commit Message
The attached patch adds support to the middle end for detecting
infinitely recursive calls. The warning is controlled by the new
-Winfinite-recursion option. The option name is the same as
Clang's.
I scheduled the warning pass to run after early inlining to
detect mutually recursive calls but before tail recursion which
turns some recursive calls into infinite loops and so makes
the two indistinguishable.
The warning detects a superset of problems detected by Clang
(based on its tests). It detects the problem in PR88232
(the feature request) as well as the one in PR 87742,
an unrelated problem report that was root-caused to bug due
to infinite recursion.
This initial version doesn't attempt to deal with superimposed
symbols, so those might trigger false positives. I'm not sure
that's something to worry about.
The tests are very light, but those for the exceptional cases
are exceedingly superficial, so it's possible those might harbor
some false positives and negatives.
Tested on x86_64-linux.
Martin
Comments
On Tue, Nov 09, 2021 at 09:28:43PM -0700, Martin Sebor via Gcc-patches wrote:
> The attached patch adds support to the middle end for detecting
> infinitely recursive calls. The warning is controlled by the new
> -Winfinite-recursion option. The option name is the same as
> Clang's.
>
> I scheduled the warning pass to run after early inlining to
> detect mutually recursive calls but before tail recursion which
> turns some recursive calls into infinite loops and so makes
> the two indistinguishable.
>
> The warning detects a superset of problems detected by Clang
> (based on its tests). It detects the problem in PR88232
> (the feature request) as well as the one in PR 87742,
> an unrelated problem report that was root-caused to bug due
> to infinite recursion.
Nice, I've long wanted this warning. I've made this mistake a couple of
times:
struct S {
operator int() { return S{}; }
};
and the patch warns about it.
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -629,6 +629,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.
Why do you need this hunk?
> new file mode 100644
> index 00000000000..324e63b2651
> --- /dev/null
> +++ b/gcc/gimple-warn-recursion.c
> @@ -0,0 +1,146 @@
> +/* -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 "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 *ctxt)
> + : gimple_opt_pass (warn_recursion_data, ctxt)
> + {}
> +
> + virtual bool gate (function *) { return warn_infinite_recursion; }
> +
> + virtual unsigned int execute (function *);
> +
> +};
> +
> +/* Return true if there is path from BB to EXIT_BB along which there is
> + no (recursive) call to FNDECL. Use VISITED to avoid searching already
> + visited basic blocks. */
> +
> +static bool
> +find_function_exit (tree fndecl, basic_block bb, int flags,
> + basic_block exit_bb,
> + auto_vec<gimple *> &calls, auto_bitmap &visited)
> +{
> + if (!bitmap_set_bit (visited, bb->index))
> + return false;
> +
> + if (bb == exit_bb)
> + 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;
> +
> + tree callee = gimple_call_fndecl (stmt);
> + if (!fndecl || fndecl != callee)
> + continue;
> +
> + /* Add the recursive call to the vector and return false. */
> + 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)
> + {
> + int eflags = e->flags;
> + if (find_function_exit (fndecl, e->dest, eflags, exit_bb, calls, visited))
Why not use e->flags directly? find_function_exit can't change it AFAICS.
> + return true;
> +
> + flags = 0;
> + }
> +
> + return (flags & EDGE_EH) != 0;
> +}
> +
> +
> +/* 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;
> + basic_block exit_bb = EXIT_BLOCK_PTR_FOR_FN (func);
> + basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (func);
> +
> + if (find_function_exit (func->decl, entry_bb, 0, exit_bb, calls, visited)
> + || calls.length () == 0)
> + return 0;
> +
> + location_t loc = DECL_SOURCE_LOCATION (func->decl);
> + if (!warning_at (loc, OPT_Winfinite_recursion,
> + "infinite recursion detected"))
> + return 0;
> +
> + for (auto stmt: calls)
> + {
> + location_t loc = gimple_location (stmt);
> + if (loc == UNKNOWN_LOCATION)
> + continue;
> +
> + inform (loc, "recursive call");
> + }
I find the shadowing of 'loc' unsightly. While here, I think
if (warning_at (DECL_SOURCE_LOCATION (func->decl), OPT_Winfinite_recursion,
"infinite recursion detected"))
for (auto stmt: calls)
{
...
}
would look neater (and avoids the shadowing), but that's just my opinion.
Marek
On 11/10/21 2:27 PM, Marek Polacek wrote:
> On Tue, Nov 09, 2021 at 09:28:43PM -0700, Martin Sebor via Gcc-patches wrote:
>> The attached patch adds support to the middle end for detecting
>> infinitely recursive calls. The warning is controlled by the new
>> -Winfinite-recursion option. The option name is the same as
>> Clang's.
>>
>> I scheduled the warning pass to run after early inlining to
>> detect mutually recursive calls but before tail recursion which
>> turns some recursive calls into infinite loops and so makes
>> the two indistinguishable.
>>
>> The warning detects a superset of problems detected by Clang
>> (based on its tests). It detects the problem in PR88232
>> (the feature request) as well as the one in PR 87742,
>> an unrelated problem report that was root-caused to bug due
>> to infinite recursion.
>
> Nice, I've long wanted this warning. I've made this mistake a couple of
> times:
>
> struct S {
> operator int() { return S{}; }
> };
>
> and the patch warns about it.
Thanks for looking at it! Consider it an early Christmas present :)
Like all middle end warnings, it warns for inline functions only
if their bodies are actually emitted. To handle the others we'd
need to also implement the warning in the front end. Or, "fake"-
emit them somehow as I think Jason was suggesting for
the -Wuninitialized warnings.
I think Clang implements it fully in the font end so it might be
doable at least for the simple subset that doesn't need to traverse
the CFG.
>
>> --- a/gcc/common.opt
>> +++ b/gcc/common.opt
>> @@ -629,6 +629,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.
>
> Why do you need this hunk?
So other languages besides the C family can control the warning.
I didn't really think about it too much, but since it's a middle
end warning it seems like they might need to (other than that,
I just copied some other warning that's in both places too).
>
>> + edge e;
>> + edge_iterator ei;
>> + FOR_EACH_EDGE (e, ei, bb->succs)
>> + {
>> + int eflags = e->flags;
>> + if (find_function_exit (fndecl, e->dest, eflags, exit_bb, calls, visited))
>
> Why not use e->flags directly? find_function_exit can't change it AFAICS.
Only because it's shorter and doesn't break up if () statement
on multiple lines. I think it's easier to read that way. But
in v2 of the patch this is simpler and not necessary anymore.
>
> I find the shadowing of 'loc' unsightly. While here, I think
>
> if (warning_at (DECL_SOURCE_LOCATION (func->decl), OPT_Winfinite_recursion,
> "infinite recursion detected"))
> for (auto stmt: calls)
> {
> ...
> }
>
> would look neater (and avoids the shadowing), but that's just my opinion.
I didn't even notice it but sure.
After thinking about the exceptional handling and taking a closer
look at what Clang warns for I decided that what I had done was
overly conservative and adjusted things to diagnose more of
the same C++ code as it does. I'll post an update shortly,
once it finished testing.
Martin
On Thu, Nov 11, 2021 at 11:21:01AM -0700, Martin Sebor wrote:
> On 11/10/21 2:27 PM, Marek Polacek wrote:
> > On Tue, Nov 09, 2021 at 09:28:43PM -0700, Martin Sebor via Gcc-patches wrote:
> > > The attached patch adds support to the middle end for detecting
> > > infinitely recursive calls. The warning is controlled by the new
> > > -Winfinite-recursion option. The option name is the same as
> > > Clang's.
> > >
> > > I scheduled the warning pass to run after early inlining to
> > > detect mutually recursive calls but before tail recursion which
> > > turns some recursive calls into infinite loops and so makes
> > > the two indistinguishable.
> > >
> > > The warning detects a superset of problems detected by Clang
> > > (based on its tests). It detects the problem in PR88232
> > > (the feature request) as well as the one in PR 87742,
> > > an unrelated problem report that was root-caused to bug due
> > > to infinite recursion.
> >
> > Nice, I've long wanted this warning. I've made this mistake a couple of
> > times:
> >
> > struct S {
> > operator int() { return S{}; }
> > };
> >
> > and the patch warns about it.
>
> Thanks for looking at it! Consider it an early Christmas present :)
>
> Like all middle end warnings, it warns for inline functions only
> if their bodies are actually emitted. To handle the others we'd
> need to also implement the warning in the front end. Or, "fake"-
> emit them somehow as I think Jason was suggesting for
> the -Wuninitialized warnings.
Yea, we're probably back at https://gcc.gnu.org/PR21678
> I think Clang implements it fully in the font end so it might be
> doable at least for the simple subset that doesn't need to traverse
> the CFG.
I think so too, but it comes with the usual caveats about implementing
warnings too early/too late.
> > > --- a/gcc/common.opt
> > > +++ b/gcc/common.opt
> > > @@ -629,6 +629,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.
> >
> > Why do you need this hunk?
>
> So other languages besides the C family can control the warning.
> I didn't really think about it too much, but since it's a middle
> end warning it seems like they might need to (other than that,
> I just copied some other warning that's in both places too).
It could be marked as Common in common.opt and then you don't need
it in c.opt. Except you do because you can't do EnabledBy(Wall) in
common.opt :(.
> > > + edge e;
> > > + edge_iterator ei;
> > > + FOR_EACH_EDGE (e, ei, bb->succs)
> > > + {
> > > + int eflags = e->flags;
> > > + if (find_function_exit (fndecl, e->dest, eflags, exit_bb, calls, visited))
> >
> > Why not use e->flags directly? find_function_exit can't change it AFAICS.
>
> Only because it's shorter and doesn't break up if () statement
> on multiple lines. I think it's easier to read that way. But
> in v2 of the patch this is simpler and not necessary anymore.
>
> >
> > I find the shadowing of 'loc' unsightly. While here, I think
> >
> > if (warning_at (DECL_SOURCE_LOCATION (func->decl), OPT_Winfinite_recursion,
> > "infinite recursion detected"))
> > for (auto stmt: calls)
> > {
> > ...
> > }
> >
> > would look neater (and avoids the shadowing), but that's just my opinion.
>
> I didn't even notice it but sure.
>
> After thinking about the exceptional handling and taking a closer
> look at what Clang warns for I decided that what I had done was
> overly conservative and adjusted things to diagnose more of
> the same C++ code as it does. I'll post an update shortly,
> once it finished testing.
Aha, ok.
Marek
Hi!
On 2021-11-09T21:28:43-0700, Martin Sebor via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> The attached patch adds support to the middle end for detecting
> infinitely recursive calls. The warning is controlled by the new
> -Winfinite-recursion option. The option name is the same as
> Clang's.
Thanks!
> The warning detects a superset of problems detected by Clang
> (based on its tests). It detects the problem in PR88232
> (the feature request) as well as the one in PR 87742,
> an unrelated problem report that was root-caused to bug due
> to infinite recursion.
... and I'm thus confirming that it also would have caught
PR101204 "infinite recursion in gtype-desc.c since
r12-1801-g7036e9ef462fde8181bece4ac4e03f3aa27204dc":
[...]
gtype-desc.c: In function ‘void gt_pch_nx(int_hash<unsigned int, 0, 4294967295>*, gt_pointer_operator, void*)’:
gtype-desc.c:11311:1: error: infinite recursion detected [-Werror=infinite-recursion]
11311 | gt_pch_nx (int_hash<location_t,0,UINT_MAX>* x ATTRIBUTE_UNUSED,
| ^~~~~~~~~
gtype-desc.c:11315:15: note: recursive call
11315 | gt_pch_nx (&((*x)), op, cookie);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
make[3]: *** [gtype-desc.o] Error 1
make[3]: Leaving directory `[...]/build-gcc/gcc'
make[2]: *** [all-stage2-gcc] Error 2
make[2]: Leaving directory `[...]/build-gcc'
make[1]: *** [stage2-bubble] Error 2
make[1]: Leaving directory `[...]/build-gcc'
make: *** [all] Error 2
That's with recent fix-up commit c71cb26a9e841888f52e4bfcaad94c8f8ecb4fdb
"Get rid of infinite recursion for 'typedef' used with GTY-marked
'gcc/diagnostic-spec.h:nowarn_map' [PR101204, PR103157]" temporarily
reverted (and commit f861ed8b29a5eb6164d1ddbcfbb6232dddae713f
"Use 'location_hash' for 'gcc/diagnostic-spec.h:nowarn_map'" as a
prerequisite, too).
Grüße
Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
On 11/24/21 3:16 AM, Thomas Schwinge wrote:
> Hi!
>
> On 2021-11-09T21:28:43-0700, Martin Sebor via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>> The attached patch adds support to the middle end for detecting
>> infinitely recursive calls. The warning is controlled by the new
>> -Winfinite-recursion option. The option name is the same as
>> Clang's.
>
> Thanks!
>
>> The warning detects a superset of problems detected by Clang
>> (based on its tests). It detects the problem in PR88232
>> (the feature request) as well as the one in PR 87742,
>> an unrelated problem report that was root-caused to bug due
>> to infinite recursion.
>
> ... and I'm thus confirming that it also would have caught
> PR101204 "infinite recursion in gtype-desc.c since
> r12-1801-g7036e9ef462fde8181bece4ac4e03f3aa27204dc":
I'd hoped it would pick it up but didn't take the time to verify
it did. Thanks for double-checking that!
Martin
>
> [...]
> gtype-desc.c: In function ‘void gt_pch_nx(int_hash<unsigned int, 0, 4294967295>*, gt_pointer_operator, void*)’:
> gtype-desc.c:11311:1: error: infinite recursion detected [-Werror=infinite-recursion]
> 11311 | gt_pch_nx (int_hash<location_t,0,UINT_MAX>* x ATTRIBUTE_UNUSED,
> | ^~~~~~~~~
> gtype-desc.c:11315:15: note: recursive call
> 11315 | gt_pch_nx (&((*x)), op, cookie);
> | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
> cc1plus: all warnings being treated as errors
> make[3]: *** [gtype-desc.o] Error 1
> make[3]: Leaving directory `[...]/build-gcc/gcc'
> make[2]: *** [all-stage2-gcc] Error 2
> make[2]: Leaving directory `[...]/build-gcc'
> make[1]: *** [stage2-bubble] Error 2
> make[1]: Leaving directory `[...]/build-gcc'
> make: *** [all] Error 2
>
> That's with recent fix-up commit c71cb26a9e841888f52e4bfcaad94c8f8ecb4fdb
> "Get rid of infinite recursion for 'typedef' used with GTY-marked
> 'gcc/diagnostic-spec.h:nowarn_map' [PR101204, PR103157]" temporarily
> reverted (and commit f861ed8b29a5eb6164d1ddbcfbb6232dddae713f
> "Use 'location_hash' for 'gcc/diagnostic-spec.h:nowarn_map'" as a
> prerequisite, too).
>
>
> Grüße
> Thomas
> -----------------
> Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
>
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.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.
@@ -629,6 +629,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 @r{(C, C++, Objective-C and Objective-C++ only)}
+@opindex Winfinite-recursion
+@opindex Wno-infinite-recursion
+Warn about infinitely recursive calls. The warning is effective at all
+optimization levels but it 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,146 @@
+/* -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 "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 *ctxt)
+ : gimple_opt_pass (warn_recursion_data, ctxt)
+ {}
+
+ virtual bool gate (function *) { return warn_infinite_recursion; }
+
+ virtual unsigned int execute (function *);
+
+};
+
+/* Return true if there is path from BB to EXIT_BB along which there is
+ no (recursive) call to FNDECL. Use VISITED to avoid searching already
+ visited basic blocks. */
+
+static bool
+find_function_exit (tree fndecl, basic_block bb, int flags,
+ basic_block exit_bb,
+ auto_vec<gimple *> &calls, auto_bitmap &visited)
+{
+ if (!bitmap_set_bit (visited, bb->index))
+ return false;
+
+ if (bb == exit_bb)
+ 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;
+
+ tree callee = gimple_call_fndecl (stmt);
+ if (!fndecl || fndecl != callee)
+ continue;
+
+ /* Add the recursive call to the vector and return false. */
+ 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)
+ {
+ int eflags = e->flags;
+ if (find_function_exit (fndecl, e->dest, eflags, exit_bb, calls, visited))
+ return true;
+
+ flags = 0;
+ }
+
+ return (flags & EDGE_EH) != 0;
+}
+
+
+/* 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;
+ basic_block exit_bb = EXIT_BLOCK_PTR_FOR_FN (func);
+ basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (func);
+
+ if (find_function_exit (func->decl, entry_bb, 0, exit_bb, calls, visited)
+ || calls.length () == 0)
+ return 0;
+
+ location_t loc = DECL_SOURCE_LOCATION (func->decl);
+ if (!warning_at (loc, OPT_Winfinite_recursion,
+ "infinite recursion detected"))
+ return 0;
+
+ for (auto stmt: 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,125 @@
+/* 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 ();
+}
+
+
+struct X
+{
+ X (); // might throw
+};
+
+void nowarn_except ()
+{
+ X x;
+ nowarn_except ();
+}
+
+
+void nowarn_try_catch ()
+{
+ try
+ {
+ X x;
+ nowarn_try_catch ();
+ }
+ catch (...)
+ {
+ }
+}
+
+
+void
+warn_try_try_catch_catch () // { dg-warning "-Winfinite-recursion" }
+{
+ try
+ {
+ try
+ {
+ X x;
+ warn_try_try_catch_catch ();
+ }
+ catch (...)
+ {
+ warn_try_try_catch_catch ();
+ }
+ }
+ catch (...)
+ {
+ // unreachable
+ }
+}
+
+
+void
+warn_try_catch_rethrow () // { dg-warning "-Winfinite-recursion" }
+{
+ try
+ {
+ X x;
+ warn_try_catch_rethrow (); // { dg-message "recursive call" }
+ }
+ catch (...)
+ {
+ warn_try_catch_rethrow (); // { dg-message "recursive call" }
+ throw; // unreachable
+ }
+}
+
+
+void
+warn_try_catch () // { dg-warning "-Winfinite-recursion" }
+{
+ try
+ {
+ X x;
+ warn_try_catch (); // { dg-message "recursive call" }
+ }
+ catch (...)
+ {
+ warn_try_catch (); // { dg-message "recursive call" }
+ }
+}
+
+void
+warn_try_catch_call () // { dg-warning "-Winfinite-recursion" }
+{
+ try
+ {
+ X x;
+ warn_try_catch_call (); // { dg-message "recursive call" }
+ }
+ catch (...) { }
+
+ warn_try_catch_call (); // { dg-message "recursive call" }
+}
new file mode 100644
@@ -0,0 +1,25 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Winfinite-recursion" } */
+
+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,46 @@
+/* PR middle-end/88232 - Please implement -Winfinite-recursion
+ { dg-do compile }
+ { dg-options "-Wall -Winfinite-recursion" } */
+
+extern int ei;
+
+int warn_fi_v (void) // { dg-warning "-Winfinite-recursion" }
+{
+ return warn_fi_v (); // { dg-message "recursive call" }
+}
+
+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;
+}
+
+/* Verify there's no warning for a function that doesn't return. */
+int nowarn_call_noreturn (void)
+{
+ __attribute__ ((noreturn)) void fnoreturn (void);
+
+ fnoreturn ();
+}
@@ -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);