Since 5.2, GCC has deferred instantiation of constexpr functions until the point that the definition is necessary for evaluating a constant-expression. The resolution of core issue 1581 says that we need to be a bit more eager about instantiation: we should instantiate a constexpr function that is mentioned in an expression that we evaluate as a constant-expression, even if the constexpr function isn't actually needed.
While playing with this I noticed two other issues; these patches aren't necessary for the final 1581 patch, but still seem correct. * 58281 was a problem with nothing ever clearing DECL_EXTERNAL on the instantiation of f; the patch for 65942 fixed it by deferring instantiation long enough for mark_used to send f to note_vague_linkage_fn so that EOF processing would clear DECL_EXTERNAL, but it would be good to clear it directly when we see the second explicit instantiation. * A function defaulted in class is just as defined as one defaulted out of class. Tested x86_64-pc-linux-gnu, applying to trunk.
commit d0c8a45932d244d27201505c8c3d52bd3dbc2a67 Author: Jason Merrill <ja...@redhat.com> Date: Thu May 31 17:09:25 2018 -0400 CWG 1581: When are constexpr member functions defined? * constexpr.c (instantiate_cx_fn_r, instantiate_constexpr_fns): New. (cxx_eval_outermost_constant_expr): Call instantiate_constexpr_fns. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index a099408dd28..944c1cdf11e 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -4813,6 +4813,46 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return r; } +/* P0859: A function is needed for constant evaluation if it is a constexpr + function that is named by an expression ([basic.def.odr]) that is + potentially constant evaluated. + + So we need to instantiate any constexpr functions mentioned by the + expression even if the definition isn't needed for evaluating the + expression. */ + +static tree +instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/) +{ + if (TREE_CODE (*tp) == FUNCTION_DECL + && DECL_DECLARED_CONSTEXPR_P (*tp) + && !DECL_INITIAL (*tp) + && DECL_TEMPLOID_INSTANTIATION (*tp)) + { + ++function_depth; + instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); + --function_depth; + } + else if (TREE_CODE (*tp) == CALL_EXPR + || TREE_CODE (*tp) == AGGR_INIT_EXPR) + { + if (EXPR_HAS_LOCATION (*tp)) + input_location = EXPR_LOCATION (*tp); + } + + if (!EXPR_P (*tp)) + *walk_subtrees = 0; + + return NULL_TREE; +} +static void +instantiate_constexpr_fns (tree t) +{ + location_t loc = input_location; + cp_walk_tree_without_duplicates (&t, instantiate_cx_fn_r, NULL); + input_location = loc; +} + static tree cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, bool strict = true, tree object = NULL_TREE) @@ -4858,6 +4898,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, r = TARGET_EXPR_INITIAL (r); } + instantiate_constexpr_fns (r); r = cxx_eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); @@ -4959,6 +5000,7 @@ is_sub_constant_expr (tree t) constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, true, true }; + instantiate_constexpr_fns (t); cxx_eval_constant_expression (&ctx, t, false, &non_constant_p, &overflow_p); return !non_constant_p && !overflow_p; diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-inst1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-inst1.C new file mode 100644 index 00000000000..1016bec9d3e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-inst1.C @@ -0,0 +1,13 @@ +// Testcase from P0859 +// { dg-do compile { target c++14 } } + +template<typename T> constexpr int f() { return T::value; } // { dg-error "int" } +template<bool B, typename T> void g(decltype(B ? f<T>() : 0)); +template<bool B, typename T> void g(...); +template<bool B, typename T> void h(decltype(int{B ? f<T>() : 0})); +template<bool B, typename T> void h(...); +void x() { + g<false, int>(0); // OK, B ? f<T>() : 0 is not potentially constant evaluated + h<false, int>(0); // error, instantiates f<int> even though B evaluates to false and + // list-initialization of int from int cannot be narrowing +}
commit 34c29b27b770d76de9a55682d46670e0bcbfe98c Author: Jason Merrill <ja...@redhat.com> Date: Thu May 31 17:21:31 2018 -0400 PR c++/58281 - explicit instantiation of constexpr * pt.c (mark_decl_instantiated): Clear DECL_EXTERNAL. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index b97cd3013be..4c5890deeb8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -22168,6 +22168,12 @@ mark_decl_instantiated (tree result, int extern_p) linkonce sections. */ else if (TREE_PUBLIC (result)) maybe_make_one_only (result); + if (TREE_CODE (result) == FUNCTION_DECL + && DECL_TEMPLATE_INSTANTIATED (result)) + /* If the function has already been instantiated, clear DECL_EXTERNAL, + since start_preparsed_function wouldn't have if we had an earlier + extern explicit instantiation. */ + DECL_EXTERNAL (result) = 0; } /* If EXTERN_P, then this function will not be emitted -- unless
commit 056cef81b097863c4683ec75abc6447840e024b3 Author: Jason Merrill <ja...@redhat.com> Date: Thu May 31 16:30:34 2018 -0400 * pt.c (instantiate_decl): Any defaulted function is defined. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index d0fc9ee51a5..b97cd3013be 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -23748,7 +23748,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) deleted_p = DECL_DELETED_FN (code_pattern); pattern_defined = ((DECL_SAVED_TREE (code_pattern) != NULL_TREE && DECL_INITIAL (code_pattern) != error_mark_node) - || DECL_DEFAULTED_OUTSIDE_CLASS_P (code_pattern) + || DECL_DEFAULTED_FN (code_pattern) || deleted_p); } else