The constexpr lambda change introduced some problematic dependency ordering, since instantiate constexpr functions aggressively so that they are available for constexpr evaluation. The patch for 65942 delayed that instantiation by triggering it from constexpr evaluation directly rather than earlier in mark_used, but instantiate_decl still wanted to instantiate them right away. This patch fixes that and a latent bug in tsubst_friend_function, which was leaving DECL_INITIAL set on a function that was not yet instantiated.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit aea89e80dc0b7b976fd69680771a8534af265d7e Author: Jason Merrill <ja...@redhat.com> Date: Thu Nov 10 10:13:44 2016 -0800 PR c++/77337 - auto return and lambda * pt.c (tsubst_friend_function): Don't set DECL_INITIAL. (instantiate_decl): It's OK to defer a constexpr function. * cp-tree.h (DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION): Check DECL_LANG_SPECIFIC. * decl2.c (decl_defined_p): Use it. No longer static. * decl.c (redeclaration_error_message): Use decl_defined_p. * constexpr.c (cxx_eval_call_expression): Set input_location around call to instantiate_decl. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 43457d2..f75f0b0 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1464,9 +1464,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, if (!DECL_INITIAL (fun) && DECL_TEMPLOID_INSTANTIATION (fun)) { + location_t save_loc = input_location; + input_location = loc; ++function_depth; instantiate_decl (fun, /*defer_ok*/false, /*expl_inst*/false); --function_depth; + input_location = save_loc; } /* If in direct recursive call, optimize definition search. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 9b5b5bc..8183775 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4380,7 +4380,8 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) instantiated will not be a DECL_TEMPLATE_INSTANTIATION, but will be a DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION. */ #define DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION(DECL) \ - (DECL_TEMPLATE_INFO (DECL) && !DECL_USE_TEMPLATE (DECL)) + (DECL_LANG_SPECIFIC (DECL) && DECL_TEMPLATE_INFO (DECL) \ + && !DECL_USE_TEMPLATE (DECL)) /* Nonzero if DECL is a function generated from a function 'temploid', i.e. template, member of class template, or dependent friend. */ @@ -5895,6 +5896,7 @@ extern void import_export_decl (tree); extern tree build_cleanup (tree); extern tree build_offset_ref_call_from_tree (tree, vec<tree, va_gc> **, tsubst_flags_t); +extern bool decl_defined_p (tree); extern bool decl_constant_var_p (tree); extern bool decl_maybe_constant_var_p (tree); extern void no_linkage_error (tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 4b18d4e..185c98b 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -2778,8 +2778,8 @@ redeclaration_error_message (tree newdecl, tree olddecl) warn_extern_redeclared_static. */ /* Defining the same name twice is no good. */ - if (DECL_INITIAL (olddecl) != NULL_TREE - && DECL_INITIAL (newdecl) != NULL_TREE) + if (decl_defined_p (olddecl) + && decl_defined_p (newdecl)) { if (DECL_NAME (olddecl) == NULL_TREE) return G_("%q#D not declared in class"); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index e0fff1e..4ebc7dc 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -80,7 +80,6 @@ static void import_export_class (tree); static tree get_guard_bits (tree); static void determine_visibility_from_class (tree, tree); static bool determine_hidden_inline (tree); -static bool decl_defined_p (tree); static void maybe_instantiate_decl (tree); /* A list of static class variables. This is needed, because a @@ -4085,11 +4084,15 @@ collect_ada_namespace (tree namespc, const char *source_file) /* Returns true iff there is a definition available for variable or function DECL. */ -static bool +bool decl_defined_p (tree decl) { if (TREE_CODE (decl) == FUNCTION_DECL) - return (DECL_INITIAL (decl) != NULL_TREE); + return (DECL_INITIAL (decl) != NULL_TREE + /* A pending instantiation of a friend temploid is defined. */ + || (DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (decl) + && DECL_INITIAL (DECL_TEMPLATE_RESULT + (DECL_TI_TEMPLATE (decl))))); else { gcc_assert (VAR_P (decl)); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index e8b6afd..d4855d5 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -9383,10 +9383,6 @@ tsubst_friend_function (tree decl, tree args) else new_friend_result_template_info = NULL_TREE; - /* Make the init_value nonzero so pushdecl knows this is a defn. */ - if (new_friend_is_defn) - DECL_INITIAL (new_friend) = error_mark_node; - /* Inside pushdecl_namespace_level, we will push into the current namespace. However, the friend function should go into the namespace of the template. */ @@ -22086,8 +22082,7 @@ instantiate_decl (tree d, int defer_ok, case that an expression refers to the value of the variable -- if the variable has a constant value the referring expression can take advantage of that fact. */ - if (VAR_P (d) - || DECL_DECLARED_CONSTEXPR_P (d)) + if (VAR_P (d)) defer_ok = 0; /* Don't instantiate cloned functions. Instead, instantiate the diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn33.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn33.C new file mode 100644 index 0000000..cfd8498 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn33.C @@ -0,0 +1,27 @@ +// PR c++/77337 +// { dg-do compile { target c++14 } } + +template<typename Functor> +struct fix_type { + Functor functor; + + decltype(auto) operator()() + { return functor(*this); } +}; + +template<typename Functor> +fix_type<Functor> fix(Functor functor) +{ return { functor }; } + +int main() +{ + auto zero = fix + ([](auto& self) -> int // N.B. non-deduced, non-dependent return type + { + return 0; + + self(); // error: use of 'decltype(auto) fix_type<Functor>::operator()() [with Functor = main()::<lambda(auto:1&)>]' before deduction of 'auto' + }); + + return zero(); +}