diff mbox series

C++ PATCH for c++/85149, generic lambda and constexpr if

Message ID CADzB+2=b2NArZKvj0To3Vwr05EiXomAacuGZZb62ztksgi45xA@mail.gmail.com
State New
Headers show
Series C++ PATCH for c++/85149, generic lambda and constexpr if | expand

Commit Message

Jason Merrill April 3, 2018, 5:35 p.m. UTC
Now that we partially instantiate generic lambdas, we run into an
issue with C++17 constexpr if: in the testcase, the branches of the
constexpr if become non-dependent when partially instantiated, but the
condition remains dependent.  In that situation, we mustn't substitute
into the branches, so with this patch I use the
PACK_EXPANSION_EXTRA_ARGS mechanism for constexpr if as well.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox series

Patch

commit 9b3662644031b30cae50f802abd410d2eabd81fa
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Apr 2 16:51:19 2018 -0400

            PR c++/85149 - generic lambda and constexpr if.
    
            * pt.c (build_extra_args, add_extra_args): Split from
            tsubst_pack_expansion.
            (tsubst_expr) [IF_STMT]: Use them.
            * cp-tree.h (IF_STMT_EXTRA_ARGS): New.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index db79338035d..f7bacd08c8f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4866,6 +4866,10 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define IF_SCOPE(NODE)		TREE_OPERAND (IF_STMT_CHECK (NODE), 3)
 #define IF_STMT_CONSTEXPR_P(NODE) TREE_LANG_FLAG_0 (IF_STMT_CHECK (NODE))
 
+/* Like PACK_EXPANSION_EXTRA_ARGS, for constexpr if.  IF_SCOPE is used while
+   building an IF_STMT; IF_STMT_EXTRA_ARGS is used after it is complete.  */
+#define IF_STMT_EXTRA_ARGS(NODE) IF_SCOPE (NODE)
+
 /* WHILE_STMT accessors. These give access to the condition of the
    while statement and the body of the while statement, respectively.  */
 #define WHILE_COND(NODE)	TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 284eaf3cab6..54d06fee09b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -11663,6 +11663,46 @@  extract_local_specs (tree pattern, tsubst_flags_t complain)
   return data.extra;
 }
 
+/* Extract any uses of local_specializations from PATTERN and add them to ARGS
+   for use in PACK_EXPANSION_EXTRA_ARGS.  */
+
+tree
+build_extra_args (tree pattern, tree args, tsubst_flags_t complain)
+{
+  tree extra = args;
+  if (local_specializations)
+    if (tree locals = extract_local_specs (pattern, complain))
+      extra = tree_cons (NULL_TREE, extra, locals);
+  return extra;
+}
+
+/* Apply any local specializations from PACK_EXPANSION_EXTRA_ARGS and add the
+   normal template args to ARGS.  */
+
+tree
+add_extra_args (tree extra, tree args)
+{
+  if (extra && TREE_CODE (extra) == TREE_LIST)
+    {
+      for (tree elt = TREE_CHAIN (extra); elt; elt = TREE_CHAIN (elt))
+	{
+	  /* The partial instantiation involved local declarations collected in
+	     extract_local_specs; map from the general template to our local
+	     context.  */
+	  tree gen = TREE_PURPOSE (elt);
+	  tree inst = TREE_VALUE (elt);
+	  if (DECL_P (inst))
+	    if (tree local = retrieve_local_specialization (inst))
+	      inst = local;
+	  /* else inst is already a full instantiation of the pack.  */
+	  register_local_specialization (inst, gen);
+	}
+      gcc_assert (!TREE_PURPOSE (extra));
+      extra = TREE_VALUE (extra);
+    }
+  return add_to_template_args (extra, args);
+}
+
 /* Substitute ARGS into T, which is an pack expansion
    (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION). Returns a
    TREE_VEC with the substituted arguments, a PACK_EXPANSION_* node
@@ -11686,26 +11726,7 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
   pattern = PACK_EXPANSION_PATTERN (t);
 
   /* Add in any args remembered from an earlier partial instantiation.  */
-  tree extra = PACK_EXPANSION_EXTRA_ARGS (t);
-  if (extra && TREE_CODE (extra) == TREE_LIST)
-    {
-      for (tree elt = TREE_CHAIN (extra); elt; elt = TREE_CHAIN (elt))
-	{
-	  /* The partial instantiation involved local declarations collected in
-	     extract_local_specs; map from the general template to our local
-	     context.  */
-	  tree gen = TREE_PURPOSE (elt);
-	  tree inst = TREE_VALUE (elt);
-	  if (DECL_P (inst))
-	    if (tree local = retrieve_local_specialization (inst))
-	      inst = local;
-	  /* else inst is already a full instantiation of the pack.  */
-	  register_local_specialization (inst, gen);
-	}
-      gcc_assert (!TREE_PURPOSE (extra));
-      extra = TREE_VALUE (extra);
-    }
-  args = add_to_template_args (extra, args);
+  args = add_extra_args (PACK_EXPANSION_EXTRA_ARGS (t), args);
 
   levels = TMPL_ARGS_DEPTH (args);
 
@@ -11881,11 +11902,8 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 	 have values for all the packs.  So remember these until then.  */
 
       t = make_pack_expansion (pattern, complain);
-      tree extra = args;
-      if (local_specializations)
-	if (tree locals = extract_local_specs (pattern, complain))
-	  extra = tree_cons (NULL_TREE, extra, locals);
-      PACK_EXPANSION_EXTRA_ARGS (t) = extra;
+      PACK_EXPANSION_EXTRA_ARGS (t)
+	= build_extra_args (pattern, args, complain);
       return t;
     }
   else if (unsubstituted_packs)
@@ -16485,8 +16503,24 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
     case IF_STMT:
       stmt = begin_if_stmt ();
       IF_STMT_CONSTEXPR_P (stmt) = IF_STMT_CONSTEXPR_P (t);
+      if (IF_STMT_CONSTEXPR_P (t))
+	args = add_extra_args (IF_STMT_EXTRA_ARGS (t), args);
       tmp = RECUR (IF_COND (t));
       tmp = finish_if_stmt_cond (tmp, stmt);
+      if (IF_STMT_CONSTEXPR_P (t)
+	  && instantiation_dependent_expression_p (tmp))
+	{
+	  /* We're partially instantiating a generic lambda, but the condition
+	     of the constexpr if is still dependent.  Don't substitute into the
+	     branches now, just remember the template arguments.  */
+	  do_poplevel (IF_SCOPE (stmt));
+	  IF_COND (stmt) = IF_COND (t);
+	  THEN_CLAUSE (stmt) = THEN_CLAUSE (t);
+	  ELSE_CLAUSE (stmt) = ELSE_CLAUSE (t);
+	  IF_STMT_EXTRA_ARGS (stmt) = build_extra_args (t, args, complain);
+	  add_stmt (stmt);
+	  break;
+	}
       if (IF_STMT_CONSTEXPR_P (t) && integer_zerop (tmp))
 	/* Don't instantiate the THEN_CLAUSE. */;
       else
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if17.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if17.C
new file mode 100644
index 00000000000..c6ebf1ebab2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-if17.C
@@ -0,0 +1,33 @@ 
+// PR c++/85149
+// { dg-do run }
+// { dg-additional-options -std=c++17 }
+
+template <typename T> struct is_void { static constexpr bool value = false; };
+template <> struct is_void<void> { static constexpr bool value = true; };
+
+template<typename S, typename T>
+constexpr decltype(auto) pipeline(S source, T target)
+{
+  return [=](auto... args)
+    {
+      if constexpr(false
+		   && is_void<decltype(source(args...))>::value)
+	{
+	  source(args...);
+	  return target();
+	}
+      else
+	{
+	  return target(source(args...));
+        }
+    };
+}
+
+int main() {
+  int i = 10;
+  int j = 42;
+  auto p = pipeline([&]{ return j; },
+		    [=](int val){ return val * i; });
+  if (p() != 420)
+    __builtin_abort();
+}