aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family/c-common.c
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2020-12-03 18:35:00 -0500
committerMarek Polacek <polacek@redhat.com>2020-12-07 11:54:30 -0500
commit1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8 (patch)
treed83a2760dce18de383aec51214737555dd9ad222 /gcc/c-family/c-common.c
parentffb268ffcf9f21e2981a71887324eb0aec245eea (diff)
downloadgcc-1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8.zip
gcc-1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8.tar.gz
gcc-1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8.tar.bz2
c-family: Fix hang with -Wsequence-point [PR98126]
verify_sequence_points uses verify_tree to recursively walk the subexpressions of an expression, and while recursing, it also keeps lists of expressions found after/before a sequence point. For a large expression, the list can grow significantly. And merge_tlist is at least N(n^2): for a list of length n it will iterate n(n -1) times, and call candidate_equal_p each time, and that can recurse further. warn_for_collision also has to go through the whole list. With a large-enough expression, the compilation can easily get stuck here for 24 hours. This patch is a simple kludge: if we see that the expression is overly complex, don't even try. gcc/c-family/ChangeLog: PR c++/98126 * c-common.c (verify_tree_lim_r): New function. (verify_sequence_points): Use it. Use nullptr instead of 0. gcc/testsuite/ChangeLog: PR c++/98126 * g++.dg/warn/Wsequence-point-4.C: New test.
Diffstat (limited to 'gcc/c-family/c-common.c')
-rw-r--r--gcc/c-family/c-common.c32
1 files changed, 27 insertions, 5 deletions
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index dda2352..0b348ae 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -2056,23 +2056,45 @@ verify_tree (tree x, struct tlist **pbefore_sp, struct tlist **pno_sp,
}
}
+static constexpr size_t verify_sequence_points_limit = 1024;
+
+/* Called from verify_sequence_points via walk_tree. */
+
+static tree
+verify_tree_lim_r (tree *tp, int *walk_subtrees, void *data)
+{
+ if (++*((size_t *) data) > verify_sequence_points_limit)
+ return integer_zero_node;
+
+ if (TYPE_P (*tp))
+ *walk_subtrees = 0;
+
+ return NULL_TREE;
+}
+
/* Try to warn for undefined behavior in EXPR due to missing sequence
points. */
void
verify_sequence_points (tree expr)
{
- struct tlist *before_sp = 0, *after_sp = 0;
+ tlist *before_sp = nullptr, *after_sp = nullptr;
+
+ /* verify_tree is highly recursive, and merge_tlist is O(n^2),
+ so we return early if the expression is too big. */
+ size_t n = 0;
+ if (walk_tree (&expr, verify_tree_lim_r, &n, nullptr))
+ return;
- warned_ids = 0;
- save_expr_cache = 0;
- if (tlist_firstobj == 0)
+ warned_ids = nullptr;
+ save_expr_cache = nullptr;
+ if (!tlist_firstobj)
{
gcc_obstack_init (&tlist_obstack);
tlist_firstobj = (char *) obstack_alloc (&tlist_obstack, 0);
}
- verify_tree (expr, &before_sp, &after_sp, 0);
+ verify_tree (expr, &before_sp, &after_sp, NULL_TREE);
warn_for_collisions (after_sp);
obstack_free (&tlist_obstack, tlist_firstobj);
}