OSDN Git Service

Make lambda conversion op and op() non-static.
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 23 Mar 2010 22:29:53 +0000 (22:29 +0000)
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 23 Mar 2010 22:29:53 +0000 (22:29 +0000)
* semantics.c (maybe_add_lambda_conv_op): Make non-static.
Also add the thunk function returned by the conversion op.
Mark the conversion deleted if the op() is variadic.
* decl2.c (mark_used): Give helpful message about deleted conversion.
* parser.c (cp_parser_lambda_declarator_opt): Don't make op() static.
* semantics.c (finish_this_expr): Adjust.
* mangle.c (write_closure_type_name): Adjust.
* decl.c (grok_op_properties): Don't allow it.
* call.c (build_user_type_conversion_1): No static conversion ops.
(build_op_call): Or op().

* decl2.c (change_return_type): Fix 'this' quals.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@157681 138bc75d-0d04-0410-961f-82ee72b054a4

14 files changed:
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/decl.c
gcc/cp/decl2.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
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv3.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 0e10b5b..81df987 100644 (file)
@@ -1,3 +1,19 @@
+2010-03-23  Jason Merrill  <jason@redhat.com>
+
+       Make lambda conversion op and op() non-static.
+       * semantics.c (maybe_add_lambda_conv_op): Make non-static.
+       Also add the thunk function returned by the conversion op.
+       Mark the conversion deleted if the op() is variadic.
+       * decl2.c (mark_used): Give helpful message about deleted conversion.
+       * parser.c (cp_parser_lambda_declarator_opt): Don't make op() static.
+       * semantics.c (finish_this_expr): Adjust.
+       * mangle.c (write_closure_type_name): Adjust.
+       * decl.c (grok_op_properties): Don't allow it.
+       * call.c (build_user_type_conversion_1): No static conversion ops.
+       (build_op_call): Or op().
+
+       * decl2.c (change_return_type): Fix 'this' quals.
+
 2010-03-22  Jason Merrill  <jason@redhat.com>
 
        PR c++/43333
index 7a6e104..edec6ea 100644 (file)
@@ -2944,16 +2944,11 @@ 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
@@ -2964,14 +2959,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, NULL, totype,
+                                          first_arg, NULL, totype,
                                           TYPE_BINFO (fromtype),
                                           conversion_path,
                                           flags,
                                           DEDUCE_CONV);
          else
            cand = add_function_candidate (&candidates, fn, fromtype,
-                                          first, NULL,
+                                          first_arg, NULL,
                                           TYPE_BINFO (fromtype),
                                           conversion_path,
                                           flags);
@@ -3379,29 +3374,20 @@ build_op_call (tree obj, VEC(tree,gc) **args, tsubst_flags_t complain)
        {
          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,
-                                   lfirst, *args, NULL_TREE,
+                                   first_mem_arg, *args, NULL_TREE,
                                    TYPE_BINFO (type),
                                    TYPE_BINFO (type),
                                    LOOKUP_NORMAL, DEDUCE_CALL);
          else
            add_function_candidate
-             (&candidates, fn, base, lfirst, *args, TYPE_BINFO (type),
+             (&candidates, fn, base, first_mem_arg, *args, TYPE_BINFO (type),
               TYPE_BINFO (type), LOOKUP_NORMAL);
        }
     }
 
-  /* 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, /*lookup_template_convs_p=*/true);
+  convs = lookup_conversions (type, /*lookup_template_convs_p=*/true);
 
   for (; convs; convs = TREE_CHAIN (convs))
     {
index 67377b0..e38abda 100644 (file)
@@ -10318,13 +10318,8 @@ grok_op_properties (tree decl, bool complain)
              || operator_code == ARRAY_REF
              || operator_code == NOP_EXPR)
            {
-             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;
-               }
+             error ("%qD must be a nonstatic member function", decl);
+             return false;
            }
          else
            {
index 9da69e6..90adc68 100644 (file)
@@ -156,8 +156,9 @@ change_return_type (tree new_ret, tree fntype)
   if (TREE_CODE (fntype) == FUNCTION_TYPE)
     newtype = build_function_type (new_ret, args);
   else
-    newtype = build_method_type_directly (TYPE_METHOD_BASETYPE (fntype),
-                                         new_ret, TREE_CHAIN (args));
+    newtype = build_method_type_directly
+      (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))),
+       new_ret, TREE_CHAIN (args));
   if (raises)
     newtype = build_exception_variant (newtype, raises);
   if (attrs)
@@ -4006,6 +4007,18 @@ mark_used (tree decl)
   if (TREE_CODE (decl) == FUNCTION_DECL
       && DECL_DELETED_FN (decl))
     {
+      if (DECL_ARTIFICIAL (decl))
+       {
+         if (DECL_OVERLOADED_OPERATOR_P (decl) == TYPE_EXPR
+             && LAMBDA_TYPE_P (DECL_CONTEXT (decl)))
+           {
+             /* We mark a lambda conversion op as deleted if we can't
+                generate it properly; see maybe_add_lambda_conv_op.  */
+             sorry ("converting lambda which uses %<...%> to "
+                    "function pointer");
+             return;
+           }
+       }
       error ("deleted function %q+D", decl);
       error ("used here");
       return;
index ea06bf7..3b81ea9 100644 (file)
@@ -1331,7 +1331,7 @@ write_closure_type_name (const tree type)
   MANGLE_TRACE_TREE ("closure-type-name", type);
 
   write_string ("Ul");
-  write_method_parms (parms, DECL_NONSTATIC_MEMBER_FUNCTION_P (fn), fn);
+  write_method_parms (parms, /*method_p=*/1, fn);
   write_char ('E');
   write_compact_number (LAMBDA_EXPR_DISCRIMINATOR (lambda));
 }
index d27d122..cbbfd94 100644 (file)
@@ -7390,18 +7390,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     declarator = make_id_declarator (NULL_TREE, ansi_opname (CALL_EXPR),
                                     sfk_none);
 
-    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;
+    quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr)
+            ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST);
     declarator = make_call_declarator (declarator, param_list, quals,
                                       exception_spec,
                                        /*late_return_type=*/NULL_TREE);
index 5d8b767..1678e50 100644 (file)
@@ -2104,13 +2104,17 @@ finish_this_expr (void)
 {
   tree result;
 
-  /* 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;
+  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;
+
+    }
   else if (current_function_decl
           && DECL_STATIC_FUNCTION_P (current_function_decl))
     {
@@ -5861,14 +5865,22 @@ 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, gc) *argvec;
 
-  if (!DECL_STATIC_FUNCTION_P (callop))
+  if (LAMBDA_EXPR_CAPTURE_LIST (CLASSTYPE_LAMBDA_EXPR (type)) != NULL_TREE)
     return;
 
-  rettype = build_pointer_type (TREE_TYPE (callop));
+  stattype = build_function_type (TREE_TYPE (TREE_TYPE (callop)),
+                                 FUNCTION_ARG_CHAIN (callop));
+
+  /* First build up the conversion op.  */
+
+  rettype = build_pointer_type (stattype);
   name = mangle_conv_op_name_for_type (rettype);
-  fntype = build_function_type (rettype, void_list_node);
-  fn = build_lang_decl (FUNCTION_DECL, name, fntype);
+  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);
   DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop);
 
   if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn
@@ -5883,7 +5895,39 @@ maybe_add_lambda_conv_op (tree type)
   DECL_ARTIFICIAL (fn) = 1;
   DECL_NOT_REALLY_EXTERN (fn) = 1;
   DECL_DECLARED_INLINE_P (fn) = 1;
+  DECL_ARGUMENTS (fn) = build_this_parm (fntype, TYPE_QUAL_CONST);
+  if (nested)
+    DECL_INTERFACE_KNOWN (fn) = 1;
+
+  add_method (type, fn, NULL_TREE);
+
+  /* Generic thunk code fails for varargs; we'll complain in mark_used if
+     the conversion op is used.  */
+  if (varargs_function_p (callop))
+    {
+      DECL_DELETED_FN (fn) = 1;
+      return;
+    }
+
+  /* Now build up the thunk to be returned.  */
+
+  name = get_identifier ("_FUN");
+  fn = statfn = build_lang_decl (FUNCTION_DECL, name, stattype);
+  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;
+  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;
+  DECL_ARGUMENTS (fn) = copy_list (TREE_CHAIN (DECL_ARGUMENTS (callop)));
+  for (arg = DECL_ARGUMENTS (fn); arg; arg = TREE_CHAIN (arg))
+    DECL_CONTEXT (arg) = fn;
   if (nested)
     DECL_INTERFACE_KNOWN (fn) = 1;
 
@@ -5891,17 +5935,55 @@ maybe_add_lambda_conv_op (tree type)
 
   if (nested)
     push_function_context ();
-  start_preparsed_function (fn, NULL_TREE,
+
+  /* Generate the body of the thunk.  */
+
+  start_preparsed_function (statfn, NULL_TREE,
+                           SF_PRE_PARSED | SF_INCLASS_INLINE);
+  if (DECL_ONE_ONLY (statfn))
+    {
+      /* Put the thunk in the same comdat group as the call op.  */
+      struct cgraph_node *callop_node, *thunk_node;
+      DECL_COMDAT_GROUP (statfn) = DECL_COMDAT_GROUP (callop);
+      callop_node = cgraph_node (callop);
+      thunk_node = cgraph_node (statfn);
+      gcc_assert (callop_node->same_comdat_group == NULL);
+      gcc_assert (thunk_node->same_comdat_group == NULL);
+      callop_node->same_comdat_group = thunk_node;
+      thunk_node->same_comdat_group = callop_node;
+    }
+  body = begin_function_body ();
+  compound_stmt = begin_compound_stmt (0);
+
+  arg = build1 (NOP_EXPR, TREE_TYPE (DECL_ARGUMENTS (callop)), void_zero_node);
+  argvec = make_tree_vector ();
+  VEC_quick_push (tree, argvec, arg);
+  for (arg = DECL_ARGUMENTS (statfn); arg; arg = TREE_CHAIN (arg))
+    VEC_safe_push (tree, gc, argvec, arg);
+  call = build_cxx_call (callop, VEC_length (tree, argvec),
+                        VEC_address (tree, argvec));
+  CALL_FROM_THUNK_P (call) = 1;
+  finish_return_stmt (call);
+
+  finish_compound_stmt (compound_stmt);
+  finish_function_body (body);
+
+  expand_or_defer_fn (finish_function (2));
+
+  /* Generate the body of the conversion op.  */
+
+  start_preparsed_function (convfn, 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_return_stmt (decay_conversion (statfn));
 
   finish_compound_stmt (compound_stmt);
   finish_function_body (body);
 
   expand_or_defer_fn (finish_function (2));
+
   if (nested)
     pop_function_context ();
 }
index 4dd6d08..f97f9c0 100644 (file)
@@ -1,3 +1,12 @@
+2010-03-23  Jason Merrill  <jason@redhat.com>
+
+       * g++.dg/cpp0x/lambda/lambda-const-neg.C: Adjust for non-static op().
+       * g++.dg/cpp0x/lambda/lambda-conv.C: Likewise.
+       * g++.dg/cpp0x/lambda/lambda-mangle.C: Likewise.
+       * g++.dg/cpp0x/lambda/lambda-non-const.C: Likewise.
+       * g++.dg/cpp0x/lambda/lambda-conv2.C: New.
+       * g++.dg/cpp0x/lambda/lambda-conv3.C: New.
+
 2010-03-23  Mike Stump  <mikestump@comcast.net>
 
        PR target/33120
index 0e329e5..7e7541c 100644 (file)
@@ -7,7 +7,7 @@ void call(const F& f) { f(); }
 
 int main() {
   call([] () -> void {});
-  call([] () mutable -> void {}); // { dg-message "" "declared mutable" }
+  call([] () mutable -> void {});
 
   int i = -1;
   call([&i] () -> void { i = 0; });
index fd7e401..acd7da0 100644 (file)
@@ -1,9 +1,7 @@
-// Test for conversion from stateless lambda to function pointer, which is
-// not yet part of the draft but hopefully will be after the March 2010
-// meeting.
+// Test for conversion from stateless lambda to function pointer.
 
 // { dg-options -std=c++0x }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZ1fvENUlvE_cvPFvvEEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZ1fvENKUlvE_cvPFvvEEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
 
 inline void f()
 {
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv2.C
new file mode 100644 (file)
index 0000000..fc19c99
--- /dev/null
@@ -0,0 +1,12 @@
+// Test for conversion from stateless lambda to function pointer.
+
+// { dg-options -std=c++0x }
+// { dg-do run }
+
+typedef int (*pfn)(int);
+
+int main()
+{
+  pfn p = [](int i) { return i-42; };
+  return p (42);
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv3.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv3.C
new file mode 100644 (file)
index 0000000..e4e7daf
--- /dev/null
@@ -0,0 +1,12 @@
+// Conversion to a function pointer uses a generic thunk, which doesn't
+// work properly for variadics.  Make sure that we can still use the lambda
+// normally.
+
+// { dg-options -std=c++0x }
+
+void f()
+{
+  auto l = [](...){};
+  void (*p1)(...) = l;         // { dg-bogus "sorry" "" { xfail *-*-* } }
+  l();                         // OK
+}
index 12aa849..5c9b483 100644 (file)
@@ -10,8 +10,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 "_ZZ1giENUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZ1giENUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "_ZZ1giENKUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZ1giENKUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
 
   algo([=]{return n+bef();});
   // The captured entities do not participate in <lambda-sig>
@@ -32,17 +32,17 @@ struct S {
   void f(int =
         // Type: ZN1S1fEiiEd0_UlvE_
         // Operator: _ZZN1S1fEiiEd0_NKUlvE_clEv
-// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZN1S1fEiiEd0_NUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NKUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZN1S1fEiiEd0_NKUlvE_clEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin } } } } }
         []{return 1;}()
         // Type: ZN1S1fEiiEd0_UlvE0_
         // Operator: _ZZN1S1fEiiEd0_NKUlvE0_clEv
-// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NUlvE0_clEv" } }
+// { dg-final { scan-assembler "_ZZN1S1fEiiEd0_NKUlvE0_clEv" } }
         + []{return 2;}(),
         int =
         // Type: ZN1S1fEiiEd_UlvE_
         // Operator: _ZZN1S1fEiiEd_NKUlvE_clEv
-// { dg-final { scan-assembler "_ZZN1S1fEiiEd_NUlvE_clEv" } }
+// { dg-final { scan-assembler "_ZZN1S1fEiiEd_NKUlvE_clEv" } }
         []{return 3;}());
 };
 
@@ -53,8 +53,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 "_ZN1RIiE1xMUlvE_clEv" } }
-// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZN1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
+// { dg-final { scan-assembler "_ZNK1RIiE1xMUlvE_clEv" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZNK1RIiE1xMUlvE_clEv" { target { ! { *-*-mingw* *-*-cygwin } } } } }
 
 void bar()
 {
@@ -64,7 +64,7 @@ void bar()
 }
 
 // lambdas used in non-template, non-class body initializers are internal.
-// { dg-final { scan-assembler-not "weak\[^\n\r\]*_ZNUlv" } }
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*_ZNKUlv" } }
 // { dg-final { scan-assembler-not "weak\[^\n\r\]*variable" } }
 int variable = []{return 1;}();
 
index 9ff8c02..9c2b119 100644 (file)
@@ -8,6 +8,7 @@ void call(F f) { f(); }
 
 int main() {
   call([] () -> void {});
+  call([] () mutable -> void {});
 
   int i = -1;
   call([i] () mutable -> void { i = 0; });