1 /* -Winfinite-recursion support.
2 Copyright (C) 2021-2023 Free Software Foundation, Inc.
3 Contributed by Martin Sebor <msebor@redhat.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
23 #include "coretypes.h"
27 #include "tree-pass.h"
29 #include "diagnostic-core.h"
30 // #include "tree-dfa.h"
32 #include "gimple-iterator.h"
36 const pass_data warn_recursion_data
=
38 GIMPLE_PASS
, /* type */
39 "*infinite-recursion", /* name */
40 OPTGROUP_NONE
, /* optinfo_flags */
42 PROP_ssa
, /* properties_required */
43 0, /* properties_provided */
44 0, /* properties_destroyed */
45 0, /* todo_flags_start */
46 0, /* todo_flags_finish */
49 class pass_warn_recursion
: public gimple_opt_pass
52 pass_warn_recursion (gcc::context
*);
55 bool gate (function
*) final override
{ return warn_infinite_recursion
; }
57 unsigned int execute (function
*) final override
;
59 bool find_function_exit (basic_block
);
61 /* Recursive calls found in M_FUNC. */
62 vec
<gimple
*> *m_calls
;
63 /* Basic blocks already visited in the current function. */
65 /* The current function. */
67 /* The current function code if it's (also) a built-in. */
68 built_in_function m_built_in
;
69 /* True if M_FUNC is a noreturn function. */
73 /* Initialize the pass and its members. */
75 pass_warn_recursion::pass_warn_recursion (gcc::context
*ctxt
)
76 : gimple_opt_pass (warn_recursion_data
, ctxt
),
77 m_calls (), m_visited (), m_func (), m_built_in (), noreturn_p ()
81 /* Return true if there is path from BB to M_FUNC exit point along which
82 there is no (recursive) call to M_FUNC. */
85 pass_warn_recursion::find_function_exit (basic_block bb
)
87 if (!bitmap_set_bit (m_visited
, bb
->index
))
90 if (bb
== EXIT_BLOCK_PTR_FOR_FN (m_func
))
93 /* Iterate over statements in BB, looking for a call to FNDECL. */
94 for (auto si
= gsi_start_bb (bb
); !gsi_end_p (si
); gsi_next_nondebug (&si
))
96 gimple
*stmt
= gsi_stmt (si
);
97 if (!is_gimple_call (stmt
))
100 if (gimple_call_builtin_p (stmt
, BUILT_IN_LONGJMP
))
101 /* A longjmp breaks infinite recursion. */
104 if (tree fndecl
= gimple_call_fndecl (stmt
))
106 /* A throw statement breaks infinite recursion. */
107 tree id
= DECL_NAME (fndecl
);
108 const char *name
= IDENTIFIER_POINTER (id
);
109 if (startswith (name
, "__cxa_throw"))
111 /* As does a call to POSIX siglongjmp. */
112 if (!strcmp (name
, "siglongjmp"))
116 && gimple_call_builtin_p (stmt
, BUILT_IN_NORMAL
)
117 && m_built_in
== DECL_FUNCTION_CODE (fndecl
))
120 = IDENTIFIER_POINTER (DECL_NAME (current_function_decl
));
121 /* Don't warn about gnu_inline extern inline function
122 like strcpy calling __builtin_strcpy, that is fine,
123 if some call is made (the builtin isn't expanded inline),
124 a call is made to the external definition. */
125 if (!(DECL_DECLARED_INLINE_P (current_function_decl
)
126 && DECL_EXTERNAL (current_function_decl
))
127 || strcmp (name
, cname
) == 0)
129 /* The call is being made from the definition of a built-in
130 (e.g., in a replacement of one) to itself. */
131 m_calls
->safe_push (stmt
);
139 /* A noreturn call breaks infinite recursion. */
140 int flags
= gimple_call_flags (stmt
);
141 if (flags
& ECF_NORETURN
)
145 tree callee
= gimple_call_fndecl (stmt
);
146 if (!callee
|| m_func
->decl
!= callee
)
149 /* Add the recursive call to the vector and return false. */
150 m_calls
->safe_push (stmt
);
154 /* If no call to FNDECL has been found search all BB's successors. */
157 FOR_EACH_EDGE (e
, ei
, bb
->succs
)
158 if (find_function_exit (e
->dest
))
165 /* Search FUNC for unconditionally infinitely recursive calls to self
166 and issue a warning if it is such a function. */
169 pass_warn_recursion::execute (function
*func
)
172 auto_vec
<gimple
*> calls
;
178 /* Avoid diagnosing an apparently infinitely recursive function that
179 doesn't return where the infinite recursion might be avoided by
180 a call to another noreturn function. */
181 noreturn_p
= lookup_attribute ("noreturn", DECL_ATTRIBUTES (m_func
->decl
));
183 if (fndecl_built_in_p (m_func
->decl
, BUILT_IN_NORMAL
))
184 m_built_in
= DECL_FUNCTION_CODE (m_func
->decl
);
186 m_built_in
= BUILT_IN_NONE
;
188 basic_block entry_bb
= ENTRY_BLOCK_PTR_FOR_FN (func
);
190 if (find_function_exit (entry_bb
) || m_calls
->length () == 0)
193 if (warning_at (DECL_SOURCE_LOCATION (func
->decl
),
194 OPT_Winfinite_recursion
,
195 "infinite recursion detected"))
196 for (auto stmt
: *m_calls
)
198 location_t loc
= gimple_location (stmt
);
199 if (loc
== UNKNOWN_LOCATION
)
202 inform (loc
, "recursive call");
211 make_pass_warn_recursion (gcc::context
*ctxt
)
213 return new pass_warn_recursion (ctxt
);