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

Reply via email to