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:
parent
c59ec55c34
commit
30ba058f77
15 changed files with 890 additions and 3 deletions
|
@ -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 \
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
202
gcc/gimple-warn-recursion.c
Normal 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);
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
75
gcc/testsuite/g++.dg/warn/Winfinite-recursion-2.C
Normal file
75
gcc/testsuite/g++.dg/warn/Winfinite-recursion-2.C
Normal 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>();
|
||||
}
|
77
gcc/testsuite/g++.dg/warn/Winfinite-recursion-3.C
Normal file
77
gcc/testsuite/g++.dg/warn/Winfinite-recursion-3.C
Normal 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;
|
||||
}
|
34
gcc/testsuite/g++.dg/warn/Winfinite-recursion.C
Normal file
34
gcc/testsuite/g++.dg/warn/Winfinite-recursion.C
Normal 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 ();
|
||||
}
|
252
gcc/testsuite/gcc.dg/Winfinite-recursion-2.c
Normal file
252
gcc/testsuite/gcc.dg/Winfinite-recursion-2.c
Normal 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 ();
|
||||
}
|
227
gcc/testsuite/gcc.dg/Winfinite-recursion.c
Normal file
227
gcc/testsuite/gcc.dg/Winfinite-recursion.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue