diff mbox

[1/4] Support lambda templates.

Message ID 089601a4e3883ebefbf7ab0c9140e5ef@imap.force9.net
State New
Headers show

Commit Message

Adam Butcher Sept. 9, 2013, 7:22 a.m. UTC
Hi Jason,

I've attached a patch that handles parameter packs in the conversion 
op.  I thought it best to get this reviewed independently before rolling 
up into the 'Support lambda templates' patch.

Do you think it's the right idea?  It seems to work okay but I've 
reworked the way the template op call is built (and built a separate 
call for the decltype expression).

Cheers,
Adam
commit 29891189d498f5c6e3aabac72db14b94a200182c
Author: Adam Butcher <adam@jessamine.co.uk>
Date:   Thu Aug 15 20:21:38 2013 +0100

    [Intended for rollup into 1/4]: Handle parameter packs in lambda conversion op.
    
    	* lambda.c (maybe_add_lambda_conv_op): Move decls to point of use.
    	* TODO: parm pack changelog to be merged with PATCH 1/4.

Comments

Jason Merrill Sept. 9, 2013, 2:05 p.m. UTC | #1
On 09/09/2013 03:22 AM, Adam Butcher wrote:
> I've attached a patch that handles parameter packs in the conversion
> op.  I thought it best to get this reviewed independently before rolling
> up into the 'Support lambda templates' patch.
>
> Do you think it's the right idea?  It seems to work okay but I've
> reworked the way the template op call is built (and built a separate
> call for the decltype expression).

Yes, that looks fine.

Jason
diff mbox

Patch

diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index e9bc7c5..3d17948 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -741,6 +741,22 @@  nonlambda_method_basetype (void)
   return TYPE_METHOD_BASETYPE (TREE_TYPE (fn));
 }
 
+/* Helper function for maybe_add_lambda_conv_op; build a CALL_EXPR with
+   indicated FN, and NARGS, but do not initialize the return type or any of the
+   argument slots.  */
+
+static tree
+prepare_op_call (tree fn, int nargs)
+{
+  tree t;
+
+  t = build_vl_exp (CALL_EXPR, nargs + 3);
+  CALL_EXPR_FN (t) = fn;
+  CALL_EXPR_STATIC_CHAIN (t) = NULL;
+
+  return t;
+}
+
 /* If the closure TYPE has a static op(), also add a conversion to function
    pointer.  */
 
@@ -749,9 +765,6 @@  maybe_add_lambda_conv_op (tree type)
 {
   bool nested = (current_function_decl != NULL_TREE);
   tree callop = lambda_function (type);
-  tree rettype, name, fntype, fn, body, compound_stmt;
-  tree thistype, stattype, statfn, convfn, call, arg;
-  vec<tree, va_gc> *argvec;
 
   if (LAMBDA_EXPR_CAPTURE_LIST (CLASSTYPE_LAMBDA_EXPR (type)) != NULL_TREE)
     return;
@@ -759,7 +772,7 @@  maybe_add_lambda_conv_op (tree type)
   if (processing_template_decl)
     return;
 
-  bool generic_lambda_p
+  bool const generic_lambda_p
     = (DECL_TEMPLATE_INFO (callop)
     && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop);
 
@@ -770,63 +783,127 @@  maybe_add_lambda_conv_op (tree type)
       return;
     }
 
+  /* Non-template conversion operators are defined directly with build_call_a
+     and using DIRECT_ARGVEC for arguments (including 'this').  Templates are
+     deferred and the CALL is built in-place.  In the case of a deduced return
+     call op, the decltype expression used as a substitute for the return type,
+     DECLTYPE_CALL is also built in-place.  The arguments of DECLTYPE_CALL in
+     the return expression may differ in flags from those in body CALL.  In
+     particular, parameter pack expansions are marked PACK_EXPANSION_LOCAL_P in
+     the body CALL, but not in DECLTYPE_CALL.  */
+
+  vec<tree, va_gc> *direct_argvec;
+  tree decltype_call = 0, call;
   tree fn_result = TREE_TYPE (TREE_TYPE (callop));
-  tree fn_args = copy_list (DECL_CHAIN (DECL_ARGUMENTS (callop)));
 
   if (generic_lambda_p)
     {
-      /* Construct the dependent member call for the static member function
-	 '_FUN' and remove 'auto' from its return type to allow for simple
+      /* Prepare the dependent member call for the static member function
+	 '_FUN' and, potentially, prepare another call to be used in a decltype
+	 return expression for a deduced return call op to allow for simple
 	 implementation of the conversion operator.  */
 
       tree instance = build_nop (type, null_pointer_node);
-      argvec = make_tree_vector ();
-      for (arg = fn_args; arg; arg = DECL_CHAIN (arg))
-	{
-	  mark_exp_read (arg);
-	  vec_safe_push (argvec, convert_from_reference (arg));
-	}
-
       tree objfn = build_min (COMPONENT_REF, NULL_TREE,
 			      instance, DECL_NAME (callop), NULL_TREE);
-      call = build_nt_call_vec (objfn, argvec);
+      int nargs = list_length (DECL_ARGUMENTS (callop)) - 1;
 
+      call = prepare_op_call (objfn, nargs);
       if (type_uses_auto (fn_result))
+	decltype_call = prepare_op_call (objfn, nargs);
+    }
+  else
+    {
+      direct_argvec = make_tree_vector ();
+      direct_argvec->quick_push (build1 (NOP_EXPR,
+					 TREE_TYPE (DECL_ARGUMENTS (callop)),
+					 null_pointer_node));
+    }
+
+  /* Copy CALLOP's argument list (as per 'copy_list') as FN_ARGS in order to
+     declare the static member function "_FUN" below.  For each arg append to
+     DIRECT_ARGVEC (for the non-template case) or populate the pre-allocated
+     call args (for the template case).  If a parameter pack is found, expand
+     it, flagging it as PACK_EXPANSION_LOCAL_P for the body call.  */
+
+  tree fn_args = NULL_TREE;
+  {
+    int ix = 0;
+    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
+    tree tgt;
+
+    while (src)
+      {
+	tree new_node = copy_node (src);
+
+	if (!fn_args)
+	  fn_args = tgt = new_node;
+	else
+	  {
+	    TREE_CHAIN (tgt) = new_node;
+	    tgt = new_node;
+	  }
+
+	mark_exp_read (tgt);
+
+	if (generic_lambda_p)
+	  {
+	    if (FUNCTION_PARAMETER_PACK_P (tgt))
+	      {
+		tree a = make_pack_expansion (tgt);
+		if (decltype_call)
+		  CALL_EXPR_ARG (decltype_call, ix) = copy_node (a);
+		PACK_EXPANSION_LOCAL_P (a) = true;
+		CALL_EXPR_ARG (call, ix) = a;
+	      }
+	    else
+	      {
+		tree a = convert_from_reference (tgt);
+		CALL_EXPR_ARG (call, ix) = a;
+		if (decltype_call)
+		  CALL_EXPR_ARG (decltype_call, ix) = copy_node (a);
+	      }
+	    ++ix;
+	  }
+	else
+	  vec_safe_push (direct_argvec, tgt);
+
+	src = TREE_CHAIN (src);
+      }
+  }
+
+
+  if (generic_lambda_p)
+    {
+      if (decltype_call)
 	{
 	  ++processing_template_decl;
 	  fn_result = finish_decltype_type
-	    (call, /*id_expression_or_member_access_p=*/false,
+	    (decltype_call, /*id_expression_or_member_access_p=*/false,
 	     tf_warning_or_error);
 	  --processing_template_decl;
 	}
     }
   else
     {
-      arg = build1 (NOP_EXPR, TREE_TYPE (DECL_ARGUMENTS (callop)),
-		    null_pointer_node);
-      argvec = make_tree_vector ();
-      argvec->quick_push (arg);
-      for (arg = fn_args; arg; arg = DECL_CHAIN (arg))
-	{
-	  mark_exp_read (arg);
-	  vec_safe_push (argvec, arg);
-	}
-      call = build_call_a (callop, argvec->length (), argvec->address ());
+      call = build_call_a (callop,
+			   direct_argvec->length (),
+			   direct_argvec->address ());
       CALL_FROM_THUNK_P (call) = 1;
       if (MAYBE_CLASS_TYPE_P (TREE_TYPE (call)))
 	call = build_cplus_new (TREE_TYPE (call), call, tf_warning_or_error);
     }
 
-  stattype = build_function_type (fn_result,
-				  FUNCTION_ARG_CHAIN (callop));
+  tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
 
   /* First build up the conversion op.  */
 
-  rettype = build_pointer_type (stattype);
-  name = mangle_conv_op_name_for_type (rettype);
-  thistype = cp_build_qualified_type (type, TYPE_QUAL_CONST);
-  fntype = build_method_type_directly (thistype, rettype, void_list_node);
-  fn = convfn = build_lang_decl (FUNCTION_DECL, name, fntype);
+  tree rettype = build_pointer_type (stattype);
+  tree name = mangle_conv_op_name_for_type (rettype);
+  tree thistype = cp_build_qualified_type (type, TYPE_QUAL_CONST);
+  tree fntype = build_method_type_directly (thistype, rettype, void_list_node);
+  tree convfn = build_lang_decl (FUNCTION_DECL, name, fntype);
+  tree fn = convfn;
   DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop);
 
   if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
@@ -861,7 +938,8 @@  maybe_add_lambda_conv_op (tree type)
   /* Now build up the thunk to be returned.  */
 
   name = get_identifier ("_FUN");
-  fn = statfn = build_lang_decl (FUNCTION_DECL, name, stattype);
+  tree statfn = build_lang_decl (FUNCTION_DECL, name, stattype);
+  fn = statfn;
   DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop);
   if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
       && DECL_ALIGN (fn) < 2 * BITS_PER_UNIT)
@@ -875,7 +953,7 @@  maybe_add_lambda_conv_op (tree type)
   DECL_DECLARED_INLINE_P (fn) = 1;
   DECL_STATIC_FUNCTION_P (fn) = 1;
   DECL_ARGUMENTS (fn) = fn_args;
-  for (arg = fn_args; arg; arg = DECL_CHAIN (arg))
+  for (tree arg = fn_args; arg; arg = DECL_CHAIN (arg))
     {
       /* Avoid duplicate -Wshadow warnings.  */
       DECL_NAME (arg) = NULL_TREE;
@@ -907,8 +985,8 @@  maybe_add_lambda_conv_op (tree type)
 	 ((symtab_node) cgraph_get_create_node (statfn),
           (symtab_node) cgraph_get_create_node (callop));
     }
-  body = begin_function_body ();
-  compound_stmt = begin_compound_stmt (0);
+  tree body = begin_function_body ();
+  tree compound_stmt = begin_compound_stmt (0);
   call = convert_from_reference (call);
   finish_return_stmt (call);