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.
This commit is contained in:
Martin Sebor 2021-11-23 15:30:29 -07:00
parent c59ec55c34
commit 30ba058f77
15 changed files with 890 additions and 3 deletions

View file

@ -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 \

View file

@ -738,6 +738,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.

View file

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

View file

@ -359,6 +359,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
@ -6194,6 +6195,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

202
gcc/gimple-warn-recursion.c Normal file
View file

@ -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);
}

View file

@ -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);

View file

@ -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
{

View file

@ -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
{

View file

@ -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
{

View file

@ -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>();
}

View file

@ -0,0 +1,77 @@
/* PR middle-end/88232 - Please implement -Winfinite-recursion
{ dg-do compile }
{ dg-options "-Wall -Winfinite-recursion" } */
typedef __SIZE_TYPE__ size_t;
/* 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;
}

View file

@ -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 ();
}

View file

@ -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 ();
}

View file

@ -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;
}

View file

@ -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);