Allow no-capture lambdas to convert to function pointer.
authorJason Merrill <jason@redhat.com>
Tue, 27 Oct 2009 21:58:09 +0000 (17:58 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 27 Oct 2009 21:58:09 +0000 (17:58 -0400)
* semantics.c (maybe_add_lambda_conv_op): New.
* parser.c (cp_parser_lambda_expression): Call it.
(cp_parser_lambda_declarator_opt): Make op() static if
no captures.
* mangle.c (write_closure_type_name): Adjust.
* semantics.c (finish_this_expr): Adjust.
* decl.c (grok_op_properties): Allow it.
* call.c (build_user_type_conversion_1): Handle static conversion op.
(build_op_call): And op().

From-SVN: r153617

13 files changed:
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/mangle.c
gcc/cp/parser.c
gcc/cp/semantics.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const-neg.C
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-non-const.C

index e722958..ba4caf8 100644 (file)
@@ -1,3 +1,16 @@
+2009-10-27  Jason Merrill  <jason@redhat.com>
+
+       Allow no-capture lambdas to convert to function pointer.
+       * semantics.c (maybe_add_lambda_conv_op): New.
+       * parser.c (cp_parser_lambda_expression): Call it.
+       (cp_parser_lambda_declarator_opt): Make op() static if
+       no captures.
+       * mangle.c (write_closure_type_name): Adjust.
+       * semantics.c (finish_this_expr): Adjust.
+       * decl.c (grok_op_properties): Allow it.
+       * call.c (build_user_type_conversion_1): Handle static conversion op.
+       (build_op_call): And op().
+
 2009-10-26  Jakub Jelinek  <jakub@redhat.com>
 
        PR debug/41828
index 4542804..d4bdcba 100644 (file)
@@ -2953,11 +2953,16 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags)
       for (fns = TREE_VALUE (conv_fns); fns; fns = OVL_NEXT (fns))
        {
          tree fn = OVL_CURRENT (fns);
+         tree first = first_arg;
 
          if (DECL_NONCONVERTING_P (fn)
              && (flags & LOOKUP_ONLYCONVERTING))
            continue;
 
+         /* Lambdas have a static conversion op.  */
+         if (DECL_STATIC_FUNCTION_P (fn))
+           first = NULL_TREE;
+
          /* [over.match.funcs] For conversion functions, the function
             is considered to be a member of the class of the implicit
             object argument for the purpose of defining the type of
@@ -2968,14 +2973,14 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags)
          if (TREE_CODE (fn) == TEMPLATE_DECL)
            cand = add_template_candidate (&candidates, fn, fromtype,
                                           NULL_TREE,
-                                          first_arg, NULL, totype,
+                                          first, NULL, totype,
                                           TYPE_BINFO (fromtype),
                                           conversion_path,
                                           flags,
                                           DEDUCE_CONV);
          else
            cand = add_function_candidate (&candidates, fn, fromtype,
-                                          first_arg, NULL,
+                                          first, NULL,
                                           TYPE_BINFO (fromtype),
                                           conversion_path,
                                           flags);
@@ -3382,20 +3387,30 @@ build_op_call (tree obj, VEC(tree,gc) **args, tsubst_flags_t complain)
       for (fns = BASELINK_FUNCTIONS (fns); fns; fns = OVL_NEXT (fns))
        {
          tree fn = OVL_CURRENT (fns);
+
+         tree lfirst = first_mem_arg;
+         if (DECL_STATIC_FUNCTION_P (fn))
+           lfirst = NULL_TREE;
+
          if (TREE_CODE (fn) == TEMPLATE_DECL)
            add_template_candidate (&candidates, fn, base, NULL_TREE,
-                                   first_mem_arg, *args, NULL_TREE,
+                                   lfirst, *args, NULL_TREE,
                                    TYPE_BINFO (type),
                                    TYPE_BINFO (type),
                                    LOOKUP_NORMAL, DEDUCE_CALL);
          else
            add_function_candidate
-             (&candidates, fn, base, first_mem_arg, *args, TYPE_BINFO (type),
+             (&candidates, fn, base, lfirst, *args, TYPE_BINFO (type),
               TYPE_BINFO (type), LOOKUP_NORMAL);
        }
     }
 
-  convs = lookup_conversions (type);
+  /* Rather than mess with handling static conversion ops here, just don't
+     look at conversions in lambdas.  */
+  if (LAMBDA_TYPE_P (type))
+    convs = NULL_TREE;
+  else
+    convs = lookup_conversions (type);
 
   for (; convs; convs = TREE_CHAIN (convs))
     {
index d737bdf..dc4c6b3 100644 (file)
@@ -2677,7 +2677,8 @@ add_implicitly_declared_members (tree t,
       CLASSTYPE_LAZY_COPY_CTOR (t) = 1;
     }
 
-  /* Currently only lambdas get a lazy move ctor.  */
+  /* Currently only lambdas get a lazy move ctor, but N2987 adds them for
+     other classes.  */
   if (LAMBDA_TYPE_P (t))
     CLASSTYPE_LAZY_MOVE_CTOR (t) = 1;
 
index ea28e9f..5c51a6f 100644 (file)
@@ -5073,6 +5073,7 @@ extern void apply_lambda_return_type            (tree, tree);
 extern tree add_capture                         (tree, tree, tree, bool, bool);
 extern tree add_default_capture                 (tree, tree, tree);
 extern tree lambda_expr_this_capture            (tree);
+extern void maybe_add_lambda_conv_op            (tree);
 
 /* in tree.c */
 void cp_free_lang_data                                 (tree t);
index ead3f33..33023a7 100644 (file)
@@ -10209,8 +10209,13 @@ grok_op_properties (tree decl, bool complain)
              || operator_code == ARRAY_REF
              || operator_code == NOP_EXPR)
            {
-             error ("%qD must be a nonstatic member function", decl);
-             return false;
+             if (class_type && LAMBDA_TYPE_P (class_type))
+               /* Lambdas can have static op() and conv ops.  */;
+             else
+               {
+                 error ("%qD must be a nonstatic member function", decl);
+                 return false;
+               }
            }
          else
            {
index d4bcbac..874df74 100644 (file)
@@ -1309,7 +1309,7 @@ write_closure_type_name (const tree type)
   MANGLE_TRACE_TREE ("closure-type-name", type);
 
   write_string ("Ul");
-  write_method_parms (parms, /*method_p=*/1, fn);
+  write_method_parms (parms, DECL_NONSTATIC_MEMBER_FUNCTION_P (fn), fn);
   write_char ('E');
   write_compact_number (LAMBDA_EXPR_DISCRIMINATOR (lambda));
 }
index 7fd995f..2a0cc37 100644 (file)
@@ -7090,6 +7090,8 @@ cp_parser_lambda_expression (cp_parser* parser)
       LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) = newlist;
     }
 
+    maybe_add_lambda_conv_op (type);
+
     type = finish_struct (type, /*attributes=*/NULL_TREE);
 
     parser->num_template_parameter_lists = saved_num_template_parameter_lists;
@@ -7347,15 +7349,25 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     declarator = make_id_declarator (NULL_TREE, ansi_opname (CALL_EXPR),
                                     sfk_none);
 
-    quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr)
-            ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);
+    quals = TYPE_UNQUALIFIED;
+    if (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) == NULL_TREE
+       && LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_NONE)
+      {
+       /* A lambda with no captures has a static op() and a conversion op
+          to function type.  */
+       if (LAMBDA_EXPR_MUTABLE_P (lambda_expr))
+         error ("lambda expression with no captures declared mutable");
+       return_type_specs.storage_class = sc_static;
+      }
+    else if (!LAMBDA_EXPR_MUTABLE_P (lambda_expr))
+      quals = TYPE_QUAL_CONST;
     declarator = make_call_declarator (declarator, param_list, quals,
                                       exception_spec,
                                        /*late_return_type=*/NULL_TREE);
 
     fco = grokmethod (&return_type_specs,
-                        declarator,
-                        attributes);
+                     declarator,
+                     attributes);
     DECL_INITIALIZED_IN_CLASS_P (fco) = 1;
     DECL_ARTIFICIAL (fco) = 1;
 
index 417c15f..e270a73 100644 (file)
@@ -2063,17 +2063,13 @@ finish_this_expr (void)
 {
   tree result;
 
-  if (current_class_ptr)
-    {
-      tree type = TREE_TYPE (current_class_ref);
-
-      /* In a lambda expression, 'this' refers to the captured 'this'.  */
-      if (LAMBDA_TYPE_P (type))
-        result = lambda_expr_this_capture (CLASSTYPE_LAMBDA_EXPR (type));
-      else
-        result = current_class_ptr;
-
-    }
+  /* In a lambda expression, 'this' refers to the captured 'this'.  */
+  if (current_function_decl
+      && LAMBDA_FUNCTION_P (current_function_decl))
+    result = (lambda_expr_this_capture
+             (CLASSTYPE_LAMBDA_EXPR (current_class_type)));
+  else if (current_class_ptr)
+    result = current_class_ptr;
   else if (current_function_decl
           && DECL_STATIC_FUNCTION_P (current_function_decl))
     {
@@ -5759,4 +5755,55 @@ lambda_expr_this_capture (tree lambda)
   return result;
 }
 
+/* If the closure TYPE has a static op(), also add a conversion to function
+   pointer.  */
+
+void
+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;
+
+  if (!DECL_STATIC_FUNCTION_P (callop))
+    return;
+
+  rettype = build_pointer_type (TREE_TYPE (callop));
+  name = mangle_conv_op_name_for_type (rettype);
+  fntype = build_function_type (rettype, void_list_node);
+  fn = build_lang_decl (FUNCTION_DECL, name, fntype);
+  DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop);
+
+  if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
+      && DECL_ALIGN (fn) < 2 * BITS_PER_UNIT)
+    DECL_ALIGN (fn) = 2 * BITS_PER_UNIT;
+
+  SET_OVERLOADED_OPERATOR_CODE (fn, TYPE_EXPR);
+  grokclassfn (type, fn, NO_SPECIAL);
+  set_linkage_according_to_type (type, fn);
+  rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof);
+  DECL_IN_AGGR_P (fn) = 1;
+  DECL_ARTIFICIAL (fn) = 1;
+  DECL_NOT_REALLY_EXTERN (fn) = 1;
+  DECL_DECLARED_INLINE_P (fn) = 1;
+  DECL_STATIC_FUNCTION_P (fn) = 1;
+
+  add_method (type, fn, NULL_TREE);
+
+  if (nested)
+    push_function_context ();
+  start_preparsed_function (fn, NULL_TREE,
+                           SF_PRE_PARSED | SF_INCLASS_INLINE);
+  body = begin_function_body ();
+  compound_stmt = begin_compound_stmt (0);
+
+  finish_return_stmt (decay_conversion (callop));
+
+  finish_compound_stmt (compound_stmt);
+  finish_function_body (body);
+
+  expand_or_defer_fn (finish_function (2));
+  if (nested)
+    pop_function_context ();
+}
 #include "gt-cp-semantics.h"
index 7c29cdf..6b1d818 100644 (file)
@@ -1,3 +1,10 @@
+2009-10-27  Jason Merrill  <jason@redhat.com>
+
+       * g++.dg/cpp0x/lambda/lambda-conv.C: New.
+       * g++.dg/cpp0x/lambda/lambda-const-neg.C: Adjust.
+       * g++.dg/cpp0x/lambda/lambda-mangle.C: Adjust.
+       * g++.dg/cpp0x/lambda/lambda-non-const.C: Adjust.
+
 2009-10-27  Eric Botcazou  <ebotcazou@adacore.com>
 
        * gnat.dg/null_pointer_deref1.adb: Accept Constraint_Error.
index f48c6c2..0e329e5 100644 (file)
@@ -3,11 +3,11 @@
 #include <cassert>
 
 template<typename F>
-void call(const F& f) { f(); } // { dg-error "discards qualifiers" }
+void call(const F& f) { f(); }
 
 int main() {
   call([] () -> void {});
-  call([] () mutable -> void {}); // { dg-message "" "`f' does not have const `operator()'" }
+  call([] () mutable -> void {}); // { dg-message "" "declared mutable" }
 
   int i = -1;
   call([&i] () -> void { i = 0; });
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C
new file mode 100644 (file)
index 0000000..e308248
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-options -std=c++0x }
+
+int main()
+{
+  void (*pfn)() = []{};
+}
index 9f7d5f3..aa7462b 100644 (file)
@@ -9,8 +9,8 @@ inline void g(int n) {
   // The closure type is encoded as Z1giEUlvE_.
   // The call operator of that type is _ZZ1giENKUlvE_clEv.
 
-// { dg-final { scan-assembler "_ZZ1giENKUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[ \t\]*_?_ZZ1giENKUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "_ZZ1giENUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[ \t\]*_?_ZZ1giENUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
 
   algo([=]{return n+bef();});
   // The captured entities do not participate in <lambda-sig>
@@ -31,17 +31,17 @@ struct S {
   void f(int =
         // Type: ZN1S1fEiiEd0_UlvE_
         // Operator: _ZZN1S1fEiiEd0_NKUlvE_clEv
-// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NKUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[ \t\]*_?_ZZN1S1fEiiEd0_NKUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[ \t\]*_?_ZZN1S1fEiiEd0_NUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
         []{return 1;}()
         // Type: ZN1S1fEiiEd0_UlvE0_
         // Operator: _ZZN1S1fEiiEd0_NKUlvE0_clEv
-// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NKUlvE0_clEv" } }
+// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NUlvE0_clEv" } }
         + []{return 2;}(),
         int =
         // Type: ZN1S1fEiiEd_UlvE_
         // Operator: _ZZN1S1fEiiEd_NKUlvE_clEv
-// { dg-final { scan-assembler "_ZZN1S1fEiiEd_NKUlvE_clEv" } }
+// { dg-final { scan-assembler "_ZZN1S1fEiiEd_NUlvE_clEv" } }
         []{return 3;}());
 };
 
@@ -52,8 +52,8 @@ template<typename T> int R<T>::x = []{return 1;}();
 template int R<int>::x;
 // Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
 // Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
-// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[ \t\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "_ZN1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[ \t\]*_?_ZN1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
 
 void bar()
 {
@@ -63,7 +63,7 @@ void bar()
 }
 
 // lambdas used in non-template, non-class body initializers are internal.
-// { dg-final { scan-assembler-not "weak\[^\n\r\]*_ZNKUlv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_ZNUlv" } }
 // { dg-final { scan-assembler-not "weak\[^\n\r\]*variable" } }
 int variable = []{return 1;}();
 
index 9c2b119..9ff8c02 100644 (file)
@@ -8,7 +8,6 @@ void call(F f) { f(); }
 
 int main() {
   call([] () -> void {});
-  call([] () mutable -> void {});
 
   int i = -1;
   call([i] () mutable -> void { i = 0; });