Patchwork [2/4,lambda] Support implicit conversion of a stateless generic lambda to a function pointer.

login
register
mail settings
Submitter Adam Butcher
Date Aug. 1, 2013, 12:25 p.m.
Message ID <1375359930-12871-3-git-send-email-adam@jessamine.co.uk>
Download mbox | patch
Permalink /patch/264422/
State New
Headers show

Comments

Adam Butcher - Aug. 1, 2013, 12:25 p.m.
---
 gcc/cp/lambda.c | 77 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 57 insertions(+), 20 deletions(-)
Jason Merrill - Aug. 3, 2013, 4:18 p.m.
On 08/01/2013 08:25 AM, Adam Butcher wrote:
> +    = DECL_TEMPLATE_INFO (callop)
> +    && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop;

An expression broken across lines should be parenthesized.

And let's move the building of 'call' for the non-template case up to be 
with the template case.

Otherwise, looks good!

Jason
Adam Butcher - Aug. 4, 2013, 11:45 p.m.
Hi Jason,

On 03.08.2013 17:18, Jason Merrill wrote:
> On 08/01/2013 08:25 AM, Adam Butcher wrote:
>> +    = DECL_TEMPLATE_INFO (callop)
>> +    && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop;
>
> An expression broken across lines should be parenthesized.
>
> And let's move the building of 'call' for the non-template case up to
> be with the template case.
>
Done.

> Otherwise, looks good!
>
Cheers.  What should I do about the symtab nullptr issue? 
(http://gcc.gnu.org/ml/gcc-patches/2013-07/msg00043.html)  Should I 
leave the workaround in my patch set as a standalone commit to be 
remedied later or should I try to squash it?  Or is the hack appropriate 
for some reason?

It occurs when <iostream> is included (I'm assuming it will with other 
code too) when a generic lambda is defined (whether using 'auto' params 
or an explicit template param list).  The following program is minimal 
and sufficient (with regard to the lambda expression) to cause it.  I've 
deliberately used a capturing explicit lambda template below to remove 
the possibility that the conversion op or implicit template code has 
anything to do with it.

    #include <iostream>

    int main()
    {
      int i;
      [i] <typename T> (T) {};
    }

It does not matter whether the lambda is declared within a function or 
referenced by an initializer at namespace scope.  The backtrace looks 
like this (when the nullptr checks are removed of course):

   0xaeea1f crash_signal
   	../../gcc/toplev.c:334
   0xcdff04 tree_check
   	../../gcc/tree.h:3899
   0xcdff04 decl_assembler_name_hash(tree_node const*)
   	../../gcc/tree.c:602
   0x8068cf insert_to_assembler_name_hash
	../../gcc/symtab.c:130
   0x806d91 symtab_initialize_asm_name_hash()
   	../../gcc/symtab.c:361
   0x806db8 symtab_node_for_asm(tree_node const*)
   	../../gcc/symtab.c:374
   0x810670 handle_alias_pairs
   	../../gcc/cgraphunit.c:1014
   0x81443c finalize_compilation_unit()
   	../../gcc/cgraphunit.c:2104
   0x62b7ce cp_write_global_declarations()
   	../../gcc/cp/decl2.c:4357

It occurs because at

   symtab.c:117   tree name = DECL_ASSEMBLER_NAME (node->symbol.decl);

'name' ends up being 0.  GDB gives the following for 
debug_tree(node->symbol.decl) which may give you a clue:

  <function_decl 0x7ffff4f45900 operator()
     type <method_type 0x7ffff4f46a80
         type <template_type_parm 0x7ffff4f467e0 auto type_0 type_6 VOID
             align 8 symtab 0 alias set -1 canonical type 0x7ffff5c6c9d8
            index 0 level 2 orig_level 2
             chain <type_decl 0x7ffff4f3bc38 auto>>
         type_0 type_6 QI
         size <integer_cst 0x7ffff66fbf80 constant 8>
         unit size <integer_cst 0x7ffff66fbfa0 constant 1>
         align 8 symtab 0 alias set -1 canonical type 0x7ffff4f46bd0 
method basetype <record_type 0x7ffff4f465e8 __lambda0>
         arg-types <tree_list 0x7ffff4f42ed8 value <pointer_type 
0x7ffff4f46b28>
             chain <tree_list 0x7ffff4f42e88 value <template_type_parm 
0x7ffff4f46738 T>
                 chain <tree_list 0x7ffff6707988 value <void_type 
0x7ffff6716bd0 void>>>>>
     static external autoinline decl_3 decl_5 QI file bug.cpp line 6 col 
22 align 16 context <record_type 0x7ffff4f465e8 __lambda0> initial 
<block 0x7ffff52ff9b0>
     arguments <parm_decl 0x7ffff4f44b00 __closure
         type <pointer_type 0x7ffff4f46c78 type <record_type 
0x7ffff4f469d8 __lambda0>
             readonly unsigned type_6 DI
             size <integer_cst 0x7ffff66fbdc0 constant 64>
             unit size <integer_cst 0x7ffff66fbde0 constant 8>
             align 64 symtab 0 alias set -1 canonical type 
0x7ffff4f46c78>
         readonly used unsigned DI file bug.cpp line 6 col 22 size 
<integer_cst 0x7ffff66fbdc0 64> unit size <integer_cst 0x7ffff66fbde0 8>
         align 64 context <function_decl 0x7ffff4f45900 operator()> 
arg-type <pointer_type 0x7ffff4f46c78>
         chain <parm_decl 0x7ffff4f44a80 D.29283 type 
<template_type_parm 0x7ffff4f46738 T>
             VOID file bug.cpp line 6 col 21
             align 8 context <function_decl 0x7ffff4f45900 operator()>
            >>
     result <result_decl 0x7ffff52ed5a0 D.29288 type <template_type_parm 
0x7ffff4f467e0 auto>
         ignored VOID file bug.cpp line 6 col 22
         align 8 context <function_decl 0x7ffff4f45900 operator()>>
     full-name "main()::<lambda(T)>"
     not-really-extern template-info 0x7ffff4f41c00
     struct-function 0x7ffff52cfbe0>

Do you know what could be causing this?  Presumably something that's 
generated in the non-template lambda case needs to be removed in the 
template case (or something new added)?  It is strange that it occurs 
only when <iostream> (or presumably some other headers) are included.  
With the two early outs for nullptrs in symtab.c the resulting programs 
behave as expected, I'm just not happy with that solution.

Cheers,
Adam
Jason Merrill - Aug. 5, 2013, 9:26 p.m.
On 08/04/2013 07:45 PM, Adam Butcher wrote:
> What should I do about the symtab nullptr issue?
> (http://gcc.gnu.org/ml/gcc-patches/2013-07/msg00043.html)  Should I
> leave the workaround in my patch set as a standalone commit to be
> remedied later or should I try to squash it?  Or is the hack appropriate
> for some reason?

Let's fix it.

>    0x810670 handle_alias_pairs
>        ../../gcc/cgraphunit.c:1014

This suggests that somehow the call operator template wound up in 
alias_pairs.  This is very mysterious; I really don't know why there 
would be any aliases in this testcase, much less one involving the 
operator().  The offending code should be pretty straightforward to find 
with a watchpoint on alias_pairs.

Jason

Patch

diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 98a7925..cf662bb 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -759,12 +759,9 @@  maybe_add_lambda_conv_op (tree type)
   if (processing_template_decl)
     return;
 
-  if (DECL_TEMPLATE_INFO (callop) && DECL_TEMPLATE_RESULT
-        (DECL_TI_TEMPLATE (callop)) == callop)
-    {
-      warning (0, "Conversion of a generic lambda to a function pointer is not currently implemented.");
-      return;
-    }
+  bool generic_lambda_p
+    = DECL_TEMPLATE_INFO (callop)
+    && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop;
 
   if (DECL_INITIAL (callop) == NULL_TREE)
     {
@@ -773,7 +770,38 @@  maybe_add_lambda_conv_op (tree type)
       return;
     }
 
-  stattype = build_function_type (TREE_TYPE (TREE_TYPE (callop)),
+  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 it's return type 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, arg);
+	}
+
+      tree objfn = build_min (COMPONENT_REF, NULL_TREE,
+			      instance, DECL_NAME (callop), NULL_TREE);
+      call = build_nt_call_vec (objfn, argvec);
+
+      if (type_uses_auto (fn_result))
+	{
+	  ++processing_template_decl;
+	  fn_result = finish_decltype_type
+	    (call, /*id_expression_or_member_access_p=*/false,
+	     tf_warning_or_error);
+	  --processing_template_decl;
+	}
+    }
+
+  stattype = build_function_type (fn_result,
 				  FUNCTION_ARG_CHAIN (callop));
 
   /* First build up the conversion op.  */
@@ -801,6 +829,9 @@  maybe_add_lambda_conv_op (tree type)
   if (nested)
     DECL_INTERFACE_KNOWN (fn) = 1;
 
+  if (generic_lambda_p)
+    fn = add_inherited_template_parms (fn, DECL_TI_TEMPLATE (callop));
+
   add_method (type, fn, NULL_TREE);
 
   /* Generic thunk code fails for varargs; we'll complain in mark_used if
@@ -827,8 +858,8 @@  maybe_add_lambda_conv_op (tree type)
   DECL_NOT_REALLY_EXTERN (fn) = 1;
   DECL_DECLARED_INLINE_P (fn) = 1;
   DECL_STATIC_FUNCTION_P (fn) = 1;
-  DECL_ARGUMENTS (fn) = copy_list (DECL_CHAIN (DECL_ARGUMENTS (callop)));
-  for (arg = DECL_ARGUMENTS (fn); arg; arg = DECL_CHAIN (arg))
+  DECL_ARGUMENTS (fn) = fn_args;
+  for (arg = fn_args; arg; arg = DECL_CHAIN (arg))
     {
       /* Avoid duplicate -Wshadow warnings.  */
       DECL_NAME (arg) = NULL_TREE;
@@ -837,6 +868,9 @@  maybe_add_lambda_conv_op (tree type)
   if (nested)
     DECL_INTERFACE_KNOWN (fn) = 1;
 
+  if (generic_lambda_p)
+    fn = add_inherited_template_parms (fn, DECL_TI_TEMPLATE (callop));
+
   add_method (type, fn, NULL_TREE);
 
   if (nested)
@@ -860,19 +894,22 @@  maybe_add_lambda_conv_op (tree type)
   body = begin_function_body ();
   compound_stmt = begin_compound_stmt (0);
 
-  arg = build1 (NOP_EXPR, TREE_TYPE (DECL_ARGUMENTS (callop)),
-		null_pointer_node);
-  argvec = make_tree_vector ();
-  argvec->quick_push (arg);
-  for (arg = DECL_ARGUMENTS (statfn); arg; arg = DECL_CHAIN (arg))
+  if (!generic_lambda_p)
     {
-      mark_exp_read (arg);
-      vec_safe_push (argvec, arg);
+      arg = build1 (NOP_EXPR, TREE_TYPE (DECL_ARGUMENTS (callop)),
+		    null_pointer_node);
+      argvec = make_tree_vector ();
+      argvec->quick_push (arg);
+      for (arg = DECL_ARGUMENTS (statfn); arg; arg = DECL_CHAIN (arg))
+	{
+	  mark_exp_read (arg);
+	  vec_safe_push (argvec, arg);
+	}
+      call = build_call_a (callop, argvec->length (), 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);
     }
-  call = build_call_a (callop, argvec->length (), 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);
   call = convert_from_reference (call);
   finish_return_stmt (call);