diff options
author | Alexandre Oliva <aoliva@redhat.com> | 2016-01-22 16:27:46 -0200 |
---|---|---|
committer | Alexandre Oliva <aoliva@redhat.com> | 2016-01-22 16:27:46 -0200 |
commit | 44bcd760ae19463fd85703f6bd80d6fdfcf8ce41 (patch) | |
tree | 621caf5f06942340cc289da424bbcdf28bd7a8d6 | |
parent | c0b02c428dafb80a7b223b5ef3d2c29532306256 (diff) | |
download | gcc-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.c | 31 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/pr69315.C | 34 |
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; +} |