Patchwork [1/3] Support lambda templates.

login
register
mail settings
Submitter Adam Butcher
Date Aug. 11, 2013, 7:49 p.m.
Message ID <1376250573-13753-2-git-send-email-adam@jessamine.co.uk>
Download mbox | patch
Permalink /patch/266393/
State New
Headers show

Comments

Adam Butcher - Aug. 11, 2013, 7:49 p.m.
* parser.c (cp_parser_lambda_declarator_opt): Accept template parameter
	list with std=gnu++1y.
	(cp_parser_lambda_body): Don't call 'expand_or_defer_fn' for lambda call
	operator template to avoid adding template result to symbol table.
	* lambda.c (lambda_function): Return template result if call operator is
	a template.
	(maybe_add_lambda_conv_op): Support conversion of a non-capturing lambda
	template to a function pointer.
	* decl2.c (check_member_template): Don't reject lambda call operator
	template in local [lambda] class.
	* pt.c (instantiate_class_template_1): Don't instantiate lambda call
	operator template when instantiating lambda class.
---
 gcc/cp/decl2.c  |  5 ++--
 gcc/cp/lambda.c | 87 ++++++++++++++++++++++++++++++++++++++++++++-------------
 gcc/cp/parser.c | 40 ++++++++++++++++++++++++--
 gcc/cp/pt.c     |  4 ++-
 4 files changed, 110 insertions(+), 26 deletions(-)
Jason Merrill - Aug. 12, 2013, 3:47 p.m.
On 08/11/2013 03:49 PM, Adam Butcher wrote:
> +      if (cxx_dialect < cxx1y || flag_iso)
> +	cp_parser_error (parser,
> +			 "lambda templates are only available with "
> +			 "-std=gnu++1y");

Normally we only use flag_iso to disable extensions that can change the 
meaning of conforming code, which I don't think is the case here.  For 
pure extensions, we prefer to use a pedwarn.

> +	  if (!DECL_TEMPLATE_INFO (decl) || DECL_TEMPLATE_RESULT
> +	      (DECL_TI_TEMPLATE (decl)) != decl)

This needs reformatting so that the arguments to a macro aren't to the 
left of the macro name.

Jason
Adam Butcher - Aug. 12, 2013, 11:52 p.m.
On 12.08.2013 16:47, Jason Merrill wrote:
> On 08/11/2013 03:49 PM, Adam Butcher wrote:
>> +      if (cxx_dialect < cxx1y || flag_iso)
>> +	cp_parser_error (parser,
>> +			 "lambda templates are only available with "
>> +			 "-std=gnu++1y");
>
> Normally we only use flag_iso to disable extensions that can change
> the meaning of conforming code, which I don't think is the case here.
> For pure extensions, we prefer to use a pedwarn.
>

Okay.  Though I'm not sure how to implement exactly what we discussed
before with pedwarn.  Namely:

On 07.08.2013 16:59, Jason Merrill wrote:
> On 08/07/2013 03:52 AM, Adam Butcher wrote:
>> I'm not sure which dialect guards to put these features behind 
>> though.
>> Strictly:
>>
>>   a) Generic lambdas created fully implicitly via 'auto params' 
>> should be
>>      accepted with -std=c++1y and -std=gnu++1y since it is actually 
>> spec'd by
>>      the draft.
>>
>>   b) Generic lambdas created with an explicit template parameter 
>> list should be
>>      accepted with -std=gnu++1y only.
>>
>>   c) Generalized implicit function templates should be accepted by 
>> -std=gnu++1y
>>      only.
>
> This makes sense to me.  Or perhaps add (c) to the concepts lite
> flag, when there is one.
>

Presumably with pedwarn it would need to be relaxed to simply
    "accepted with -std=c++1y or -std=gnu++1y"
for all?  flag_iso was the only thing I could find to discriminate 
between gnu++1y
and c++1y?

>> +	  if (!DECL_TEMPLATE_INFO (decl) || DECL_TEMPLATE_RESULT
>> +	      (DECL_TI_TEMPLATE (decl)) != decl)
>
> This needs reformatting so that the arguments to a macro aren't to
> the left of the macro name.
>
Done.

Adam
Jason Merrill - Aug. 13, 2013, 12:29 p.m.
On 08/12/2013 07:52 PM, Adam Butcher wrote:
> Presumably with pedwarn it would need to be relaxed to simply
>     "accepted with -std=c++1y or -std=gnu++1y"
> for all?

Yes.

> flag_iso was the only thing I could find to discriminate
> between gnu++1y and c++1y?

Right, we don't make that distinction for most extensions.

Jason

Patch

diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index d5d2912..ac9dbd7 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -507,8 +507,9 @@  check_member_template (tree tmpl)
       || (TREE_CODE (decl) == TYPE_DECL
 	  && MAYBE_CLASS_TYPE_P (TREE_TYPE (decl))))
     {
-      /* The parser rejects template declarations in local classes.  */
-      gcc_assert (!current_function_decl);
+      /* The parser rejects template declarations in local classes
+	 (with the exception of generic lambdas).  */
+      gcc_assert (!current_function_decl || LAMBDA_FUNCTION_P (decl));
       /* The parser rejects any use of virtual in a function template.  */
       gcc_assert (!(TREE_CODE (decl) == FUNCTION_DECL
 		    && DECL_VIRTUAL_P (decl)));
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index a53e692..e9bc7c5 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -196,7 +196,7 @@  lambda_function (tree lambda)
 			  /*protect=*/0, /*want_type=*/false,
 			  tf_warning_or_error);
   if (lambda)
-    lambda = BASELINK_FUNCTIONS (lambda);
+    lambda = STRIP_TEMPLATE (get_first_fn (lambda));
   return lambda;
 }
 
@@ -759,6 +759,10 @@  maybe_add_lambda_conv_op (tree type)
   if (processing_template_decl)
     return;
 
+  bool generic_lambda_p
+    = (DECL_TEMPLATE_INFO (callop)
+    && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop);
+
   if (DECL_INITIAL (callop) == NULL_TREE)
     {
       /* If the op() wasn't instantiated due to errors, give up.  */
@@ -766,7 +770,54 @@  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 its 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, 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);
+
+      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;
+	}
+    }
+  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_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));
 
   /* First build up the conversion op.  */
@@ -794,6 +845,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
@@ -820,8 +874,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;
@@ -830,6 +884,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)
@@ -852,27 +909,15 @@  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))
-    {
-      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 = convert_from_reference (call);
   finish_return_stmt (call);
 
   finish_compound_stmt (compound_stmt);
   finish_function_body (body);
 
-  expand_or_defer_fn (finish_function (2));
+  fn = finish_function (/*inline*/2);
+  if (!generic_lambda_p)
+    expand_or_defer_fn (fn);
 
   /* Generate the body of the conversion op.  */
 
@@ -888,7 +933,9 @@  maybe_add_lambda_conv_op (tree type)
   finish_compound_stmt (compound_stmt);
   finish_function_body (body);
 
-  expand_or_defer_fn (finish_function (2));
+  fn = finish_function (/*inline*/2);
+  if (!generic_lambda_p)
+    expand_or_defer_fn (fn);
 
   if (nested)
     pop_function_context ();
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c5d398a..d32608c 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -8790,6 +8790,7 @@  cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
 /* Parse the (optional) middle of a lambda expression.
 
    lambda-declarator:
+     < template-parameter-list [opt] >
      ( parameter-declaration-clause [opt] )
        attribute-specifier [opt]
        mutable [opt]
@@ -8809,10 +8810,31 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
   tree param_list = void_list_node;
   tree attributes = NULL_TREE;
   tree exception_spec = NULL_TREE;
+  tree template_param_list = NULL_TREE;
   tree t;
 
-  /* The lambda-declarator is optional, but must begin with an opening
-     parenthesis if present.  */
+  /* The template-parameter-list is optional, but must begin with
+     an opening angle if present.  */
+  if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
+    {
+      cp_lexer_consume_token (parser->lexer);
+
+      if (cxx_dialect < cxx1y || flag_iso)
+	cp_parser_error (parser,
+			 "lambda templates are only available with "
+			 "-std=gnu++1y");
+
+      template_param_list = cp_parser_template_parameter_list (parser);
+
+      cp_parser_skip_to_end_of_template_parameter_list (parser);
+
+      /* We just processed one more parameter list.  */
+      ++parser->num_template_parameter_lists;
+    }
+
+  /* The parameter-declaration-clause is optional (unless
+     template-parameter-list was given), but must begin with an
+     opening parenthesis if present.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       cp_lexer_consume_token (parser->lexer);
@@ -8858,6 +8880,8 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 
       leave_scope ();
     }
+  else if (template_param_list != NULL_TREE) // generate diagnostic
+    cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 
   /* Create the function call operator.
 
@@ -8901,6 +8925,12 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 	DECL_ARTIFICIAL (fco) = 1;
 	/* Give the object parameter a different name.  */
 	DECL_NAME (DECL_ARGUMENTS (fco)) = get_identifier ("__closure");
+	if (template_param_list)
+	  {
+	    fco = finish_member_template_decl (fco);
+	    finish_template_decl (template_param_list);
+	    --parser->num_template_parameter_lists;
+	  }
       }
 
     finish_member_declaration (fco);
@@ -9023,7 +9053,11 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
     finish_lambda_scope ();
 
     /* Finish the function and generate code for it if necessary.  */
-    expand_or_defer_fn (finish_function (/*inline*/2));
+    tree fn = finish_function (/*inline*/2);
+
+    /* Only expand if the call op is not a template.  */
+    if (!DECL_TEMPLATE_INFO (fco))
+      expand_or_defer_fn (fn);
   }
 
   parser->local_variables_forbidden_p = local_variables_forbidden_p;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index d03c1cf..6345e7e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -9101,7 +9101,9 @@  instantiate_class_template_1 (tree type)
       tree decl = lambda_function (type);
       if (decl)
 	{
-	  instantiate_decl (decl, false, false);
+	  if (!DECL_TEMPLATE_INFO (decl) || DECL_TEMPLATE_RESULT
+	      (DECL_TI_TEMPLATE (decl)) != decl)
+	    instantiate_decl (decl, false, false);
 
 	  /* We need to instantiate the capture list from the template
 	     after we've instantiated the closure members, but before we