summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Oliva <aoliva@redhat.com>2016-01-22 16:27:46 -0200
committerAlexandre Oliva <aoliva@redhat.com>2016-01-22 16:27:46 -0200
commit44bcd760ae19463fd85703f6bd80d6fdfcf8ce41 (patch)
tree621caf5f06942340cc289da424bbcdf28bd7a8d6
parentc0b02c428dafb80a7b223b5ef3d2c29532306256 (diff)
downloadgcc-aoliva/pr69315.tar.gz
[PR69315] enable finish_function to recurse for constexpr functionsaoliva/pr69315
We don't want finish_function to be called recursively from mark_used. However, it's desirable and necessary to call itself recursively when performing delayed folding, because that may have to instantiate and evaluate constexpr template functions. So, arrange for finish_function to accept being called recursively during delayed folding, save and restore the controlling variables, and process the deferred mark_used calls only when the outermost call completes. for gcc/cp/ChangeLog PR c++/69315 * decl.c (is_folding_function): New variable. (finish_function): Test, save and set it. for gcc/testsuite/ChangeLog PR c++/69315 * g++.dg/pr69315.C: New.
-rw-r--r--gcc/cp/decl.c31
-rw-r--r--gcc/testsuite/g++.dg/pr69315.C34
2 files changed, 59 insertions, 6 deletions
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index f4604b61ee0..65eff2fba4c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -227,10 +227,14 @@ struct GTY((for_user)) named_label_entry {
function, two inside the body of a function in a local class, etc.) */
int function_depth;
-/* To avoid unwanted recursion, finish_function defers all mark_used calls
- encountered during its execution until it finishes. */
+/* To avoid unwanted recursion, finish_function defers all mark_used
+ calls encountered during its execution until it finishes.
+ finish_function refuses to be called recursively, unless the
+ recursion occurs during folding, which often requires instantiating
+ and evaluating template functions. */
bool defer_mark_used_calls;
vec<tree, va_gc> *deferred_mark_used_calls;
+static bool is_folding_function;
/* States indicating how grokdeclarator() should handle declspecs marked
with __attribute__((deprecated)). An object declared as
@@ -14528,8 +14532,11 @@ finish_function (int flags)
if (c_dialect_objc ())
objc_finish_function ();
- gcc_assert (!defer_mark_used_calls);
+ gcc_assert (!defer_mark_used_calls
+ || (is_folding_function && DECL_DECLARED_CONSTEXPR_P (fndecl)));
defer_mark_used_calls = true;
+ bool save_folding_function = is_folding_function;
+ is_folding_function = false;
record_key_method_defined (fndecl);
@@ -14636,7 +14643,14 @@ finish_function (int flags)
/* Perform delayed folding before NRV transformation. */
if (!processing_template_decl)
- cp_fold_function (fndecl);
+ {
+ is_folding_function = true;
+ cp_fold_function (fndecl);
+ /* Check that our controlling variables were restored to the
+ expect state. */
+ gcc_assert (is_folding_function && defer_mark_used_calls);
+ is_folding_function = false;
+ }
/* Set up the named return value optimization, if we can. Candidate
variables are selected in check_return_expr. */
@@ -14780,8 +14794,13 @@ finish_function (int flags)
/* Clean up. */
current_function_decl = NULL_TREE;
- defer_mark_used_calls = false;
- if (deferred_mark_used_calls)
+ is_folding_function = save_folding_function;
+ /* Iff we were called recursively for a constexpr function,
+ is_folding_function was just restored to TRUE. If we weren't
+ called recursively, it was restored to FALSE. That's just how
+ defer_mark_used_call ought to be set. */
+ defer_mark_used_calls = is_folding_function;
+ if (!defer_mark_used_calls && deferred_mark_used_calls)
{
unsigned int i;
tree decl;
diff --git a/gcc/testsuite/g++.dg/pr69315.C b/gcc/testsuite/g++.dg/pr69315.C
new file mode 100644
index 00000000000..28975b659f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr69315.C
@@ -0,0 +1,34 @@
+// { dg-do compile }
+// { dg-options "-std=c++11" }
+
+// Template instantiation and evaluation for folding within
+// finish_function may call finish_function recursively.
+// Make sure we don't reject or delay that sort of recursion.
+
+template <bool> struct Iter;
+
+struct Arg {
+ Iter<true> begin();
+ Iter<true> end();
+};
+
+template <bool> struct Iter {
+ int operator*();
+ Iter operator++();
+ template <bool C1, bool C2> friend constexpr bool operator==(Iter<C1>, Iter<C2>);
+ template <bool C1, bool C2> friend constexpr bool operator!=(Iter<C1>, Iter<C2>);
+};
+
+void func(Arg a) {
+ for (auto ch : a) {
+ a.begin() == a.end();
+ }
+}
+
+template <bool C1, bool C2> constexpr bool operator==(Iter<C1>, Iter<C2>) {
+ return true;
+}
+
+template <bool C1, bool C2> constexpr bool operator!=(Iter<C1> a, Iter<C2> b) {
+ return a == b;
+}