Patchwork [1/3,lambda] Support template-parameter-list in lambda-declarator.

login
register
mail settings
Submitter Adam Butcher
Date July 19, 2013, 9 a.m.
Message ID <1374224442-9360-2-git-send-email-adam@jessamine.co.uk>
Download mbox | patch
Permalink /patch/260213/
State New
Headers show

Comments

Adam Butcher - July 19, 2013, 9 a.m.
---
 gcc/cp/decl2.c  |  5 +++--
 gcc/cp/lambda.c |  9 ++++++++-
 gcc/cp/parser.c | 36 ++++++++++++++++++++++++++++++++++--
 gcc/cp/pt.c     |  4 +++-
 4 files changed, 48 insertions(+), 6 deletions(-)
Jason Merrill - July 19, 2013, 4:56 p.m.
On 07/19/2013 05:00 AM, Adam Butcher wrote:
> +      warning (0, "Conversion of a generic lambda to a function pointer is not
> currently implemented.");
...
> +                        "Generic lambdas are only supported in C++1y mode.");

We generally don't capitalize diagnostics or end them with a period. 
And the second message should mention "-std=c++1y or -std=gnu++1y".

> +      push_deferring_access_checks (dk_deferred);

Why do you need this?

> +/* Nonzero if the function being declared was made a template due to it's

"its"

> +         error ("parameter declared %<auto%> (unsupported prior to C++1y)");

This should also mention the compiler flags.  We should probably 
introduce a maybe_warn_cpp1y function to go along with ..._cpp0x.

> +  else // extend current template parameter list

Do we still need to do this, now that we're handling all the parameters 
at the end of the parameter list?

Jason
Adam Butcher - Aug. 1, 2013, 12:25 p.m.
Hi Jason,

I've addressed your review comments and provided support for
conversion to function pointer for generic lambdas.  I've sent the
patches as updates to the previous set.  I'll wait for any further
comments before formalizing them into a cleaner set.

The changes now support the examples given in N3690 ยง5.1.2.{5,6} and
the test program included at the end of this mail.  I think it is
feature-complete.

On 19.07.2013 17:56, Jason Merrill wrote:
> On 07/19/2013 05:00 AM, Adam Butcher wrote:
> >
> > +      push_deferring_access_checks (dk_deferred);
> >
> Why do you need this?
>
I don't think I do.  I had thought that it would be necessary to
handle deferred friendship situations but I don't think that can apply
to generic lambdas.  I've ditched them.

> > +/* Nonzero if the function being declared was made a template due to it's
> >
> "its"
>
I've fixed this and a couple of others in my own comments.  I've also
fixed others I found in the same file (as a separate patch).

> > +  else // extend current template parameter list
> >
> Do we still need to do this, now that we're handling all the
> parameters at the end of the parameter list?
>
I think extending the current parameter list is still needed when
'auto' is found in a parameter declaration of an existing function
template.

E.g.
   template <typename T> void f(T& t, auto& a) { ... }

I think that it is necessary to keep everything consistent as if the
template had been declared in the 'conventional' way.  The template
parameter generated for the generic parameter 'a' effectively makes
'f's template parameter list into '<typename T, typename __Gen0>'.

Cheers,
Adam


Patch summary (4):
  Preserve type qualifiers for implicit template parameters.
  Support implicit conversion of a stateless generic lambda to a
    function pointer.
  Address review comments.
  Grammar "it's" to "its".

 gcc/cp/cp-tree.h |  2 +-
 gcc/cp/decl.c    |  3 ++-
 gcc/cp/lambda.c  | 77 +++++++++++++++++++++++++++++++++++++++++---------------
 gcc/cp/parser.c  |  6 ++---
 gcc/cp/pt.c      | 21 ++++++++++------
 5 files changed, 76 insertions(+), 33 deletions(-)


Test program:


  /* Function templates at namespace scope.  */
  
  auto f1 (auto& a, auto const& b) { return a += b; }
  
  template <typename A>
  auto f2 (A& a, auto const& b) { return a += b; }
  
  template <typename B>
  auto f3 (auto& a, B const& b) { return a += b; }
  
  template <typename A, typename B>
  auto f4 (A& a, B const& b) { return a += b; }
  
  
  struct S
  {
    /* Non-static member function templates.  */
  
    auto mf1 (auto& a, auto const& b) { return a += b; }
  
    template <typename A>
    auto mf2 (A& a, auto const& b) { return a += b; }
  
    template <typename B>
    auto mf3 (auto& a, B const& b) { return a += b; }
  
    template <typename A, typename B>
    auto mf4 (A& a, B const& b) { return a += b; }
  
    /* Static member function templates.  */
  
    static auto smf1 (auto& a, auto const& b) { return a += b; }
  
    template <typename A>
    static auto smf2 (A& a, auto const& b) { return a += b; }
  
    template <typename B>
    static auto smf3 (auto& a, B const& b) { return a += b; }
  
    template <typename A, typename B>
    static auto smf4 (A& a, B const& b) { return a += b; }
  };
  
  
  #undef NDEBUG
  #include <cassert>
  
  
  #define CHECK(A, b, f) do {      \
     A a1 = 5, a2 = 12;            \
     auto r1 = f (a1, b);          \
     auto r2 = f (a2, b);          \
     assert ((#f, a1 == 5 + b));   \
     assert ((#f, a2 == 12 + b));  \
     assert ((#f, r1 == a1));      \
     assert ((#f, r2 == a2));      \
  } while (0)
  
  
  #define INVOKEi(f, A, b, i) do { CHECK (A, b, f ## i); } while (0)
  #define INVOKE4(f, A, b) do { INVOKEi (f, A, b, 1); \
  			      INVOKEi (f, A, b, 2); \
  			      INVOKEi (f, A, b, 3); \
  			      INVOKEi (f, A, b, 4); } while (0)
  
  #define AS_FUNi(f, A, b, i) do { CHECK (A, b, f ## i._FUN); } while (0)
  #define AS_FUN4(f, A, b) do { AS_FUNi (f, A, b, 1); \
  			      AS_FUNi (f, A, b, 2); \
  			      AS_FUNi (f, A, b, 3); \
  			      AS_FUNi (f, A, b, 4); } while (0)
  
  #define AS_PTRi(f, A, B, b, i) do { A (*pfn) (A&, B const&) = f ## i; \
  				    CHECK (A, b, pfn); } while (0)
  #define AS_PTR4(f, A, B, b) do { AS_PTRi (f, A, B, b, 1); \
  				 AS_PTRi (f, A, B, b, 2); \
  				 AS_PTRi (f, A, B, b, 3); \
  				 AS_PTRi (f, A, B, b, 4); } while (0)
  
  
  int main()
  {
    /* Check namespace templates.  */
  
    INVOKE4 (f, float, 7);
    AS_PTR4 (f, float, int, 7);
  
    /* Check member templates.  */
  
    S s;
    INVOKE4 (s.mf, float, 7);
    INVOKE4 (s.smf, float, 7);
    INVOKE4 (S::smf, float, 7);
    AS_PTR4 (s.smf, float, int, 7);
    AS_PTR4 (S::smf, float, int, 7);
  
    /* Regression check non-template stateless lambda and its conversion
       to function pointer.  */
  
    auto lf0 = [] (float& a, int const& b) { return a += b; };
  
    INVOKEi (lf, float, 7, 0);
    AS_FUNi (lf, float, 7, 0);
    AS_PTRi (lf, float, int, 7, 0);
  
    /* Check stateless lambda templates.  */
  
    auto lf1 = [] (auto& a, auto const& b) { return a += b; };
    auto lf2 = [] <typename A> (A& a, auto const& b) { return a += b; };
    auto lf3 = [] <typename B> (auto& a, B const& b) { return a += b; };
    auto lf4 = [] <typename A, typename B> (A& a, B const& b) { return a += b; };
  
    INVOKE4 (lf, float, 7);
    AS_FUN4 (lf, float, 7);
    AS_PTR4 (lf, float, int, 7);
  
    /* Check capturing lambda templates.  */
  
    int i;
  
    auto lc1 = [i] (auto& a, auto const& b) { return a += b; };
    auto lc2 = [i] <typename A> (A& a, auto const& b) { return a += b; };
    auto lc3 = [i] <typename B> (auto& a, B const& b) { return a += b; };
    auto lc4 = [i] <typename A, typename B> (A& a, B const& b) { return a += b; };
  
    INVOKE4 (lc, float, 7);
  }

Patch

diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 1573ced..c166f6e 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..98a7925 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,13 @@  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;
+    }
+
   if (DECL_INITIAL (callop) == NULL_TREE)
     {
       /* If the op() wasn't instantiated due to errors, give up.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 4b683bf..48c95e6 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,32 @@  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)
+	cp_parser_error (parser,
+			 "Generic lambdas are only supported in C++1y mode.");
+
+      push_deferring_access_checks (dk_deferred);
+
+      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 +8881,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 +8926,13 @@  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)
+	  {
+	    pop_deferring_access_checks ();
+	    fco = finish_member_template_decl (fco);
+	    finish_template_decl (template_param_list);
+	    --parser->num_template_parameter_lists;
+	  }
       }
 
     finish_member_declaration (fco);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index de054ac..3694ccc 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -9028,7 +9028,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