]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR c++/86943 - wrong code converting lambda to function pointer.
authorJason Merrill <jason@redhat.com>
Wed, 30 Jan 2019 02:43:04 +0000 (21:43 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 30 Jan 2019 02:43:04 +0000 (21:43 -0500)
In this PR, instantiating the static thunk returned from the generic lambda
conversion function template was using normal overload resolution, which
meant calling an extra constructor when forwarding its argument.  Fixed by
special-casing thunk calls significantly more.

* lambda.c (maybe_add_lambda_conv_op): Use a template-id in the
call.  Only forward parms for decltype.
* pt.c (tsubst_copy_and_build) [CALL_EXPR]: Handle CALL_FROM_THUNK_P
specially.
* typeck.c (check_return_expr): Don't mess with a thunk call.

From-SVN: r268377

gcc/cp/ChangeLog
gcc/cp/lambda.c
gcc/cp/pt.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv13.C [new file with mode: 0644]

index 3cdc7790f435f01f337f2b00c80bbd65c67a3382..af4d9c2635efd84874d3035d8d39ccf556d22e5f 100644 (file)
@@ -1,3 +1,12 @@
+2019-01-29  Jason Merrill  <jason@redhat.com>
+
+       PR c++/86943 - wrong code converting lambda to function pointer.
+       * lambda.c (maybe_add_lambda_conv_op): Use a template-id in the
+       call.  Only forward parms for decltype.
+       * pt.c (tsubst_copy_and_build) [CALL_EXPR]: Handle CALL_FROM_THUNK_P
+       specially.
+       * typeck.c (check_return_expr): Don't mess with a thunk call.
+
 2019-01-28  Jason Merrill  <jason@redhat.com>
 
        PR c++/89089 - ICE with [[no_unique_address]].
index 6e6db1fd72ecb8fe964b48d2af15c415806db417..bc64a4173f931786ce98d687e127ce822421b955 100644 (file)
@@ -1095,8 +1095,10 @@ maybe_add_lambda_conv_op (tree type)
         implementation of the conversion operator.  */
 
       tree instance = cp_build_fold_indirect_ref (thisarg);
-      tree objfn = build_min (COMPONENT_REF, NULL_TREE,
-                             instance, DECL_NAME (callop), NULL_TREE);
+      tree objfn = lookup_template_function (DECL_NAME (callop),
+                                            DECL_TI_ARGS (callop));
+      objfn = build_min (COMPONENT_REF, NULL_TREE,
+                        instance, objfn, NULL_TREE);
       int nargs = list_length (DECL_ARGUMENTS (callop)) - 1;
 
       call = prepare_op_call (objfn, nargs);
@@ -1137,18 +1139,21 @@ maybe_add_lambda_conv_op (tree type)
 
        if (generic_lambda_p)
          {
-           /* Avoid capturing variables in this context.  */
-           ++cp_unevaluated_operand;
-           tree a = forward_parm (tgt);
-           --cp_unevaluated_operand;
-
+           tree a = tgt;
+           if (DECL_PACK_P (tgt))
+             {
+               a = make_pack_expansion (a);
+               PACK_EXPANSION_LOCAL_P (a) = true;
+             }
            CALL_EXPR_ARG (call, ix) = a;
-           if (decltype_call)
-             CALL_EXPR_ARG (decltype_call, ix) = unshare_expr (a);
 
-           if (PACK_EXPANSION_P (a))
-             /* Set this after unsharing so it's not in decltype_call.  */
-             PACK_EXPANSION_LOCAL_P (a) = true;
+           if (decltype_call)
+             {
+               /* Avoid capturing variables in this context.  */
+               ++cp_unevaluated_operand;
+               CALL_EXPR_ARG (decltype_call, ix) = forward_parm (tgt);
+               --cp_unevaluated_operand;
+             }
 
            ++ix;
          }
index f8b3054533e7405277c60c6fd2cbfcfa97c3dd1e..cb06a570d48c3c35c226b46e88aedaccb8952fc8 100644 (file)
@@ -18680,6 +18680,52 @@ tsubst_copy_and_build (tree t,
              }
          }
 
+       /* Stripped-down processing for a call in a thunk.  Specifically, in
+          the thunk template for a generic lambda.  */
+       if (CALL_FROM_THUNK_P (t))
+         {
+           tree thisarg = NULL_TREE;
+           if (TREE_CODE (function) == COMPONENT_REF)
+             {
+               thisarg = TREE_OPERAND (function, 0);
+               if (TREE_CODE (thisarg) == INDIRECT_REF)
+                 thisarg = TREE_OPERAND (thisarg, 0);
+               function = TREE_OPERAND (function, 1);
+               if (TREE_CODE (function) == BASELINK)
+                 function = BASELINK_FUNCTIONS (function);
+             }
+           /* We aren't going to do normal overload resolution, so force the
+              template-id to resolve.  */
+           function = resolve_nondeduced_context (function, complain);
+           for (unsigned i = 0; i < nargs; ++i)
+             {
+               /* In a thunk, pass through args directly, without any
+                  conversions.  */
+               tree arg = (*call_args)[i];
+               while (TREE_CODE (arg) != PARM_DECL)
+                 arg = TREE_OPERAND (arg, 0);
+               (*call_args)[i] = arg;
+             }
+           if (thisarg)
+             {
+               /* Shift the other args over to make room.  */
+               vec_safe_push (call_args, (*call_args)[nargs-1]);
+               for (int i = nargs-1; i > 0; --i)
+                 (*call_args)[i] = (*call_args)[i-1];
+               (*call_args)[0] = thisarg;
+             }
+           ret = build_call_a (function, call_args->length (),
+                               call_args->address ());
+           /* The thunk location is not interesting.  */
+           SET_EXPR_LOCATION (ret, UNKNOWN_LOCATION);
+           CALL_FROM_THUNK_P (ret) = true;
+           if (CLASS_TYPE_P (TREE_TYPE (ret)))
+             CALL_EXPR_RETURN_SLOT_OPT (ret) = true;
+
+           release_tree_vector (call_args);
+           RETURN (ret);
+         }
+
        /* We do not perform argument-dependent lookup if normal
           lookup finds a non-function, in accordance with the
           expected resolution of DR 218.  */
@@ -18883,22 +18929,12 @@ tsubst_copy_and_build (tree t,
            bool op = CALL_EXPR_OPERATOR_SYNTAX (t);
            bool ord = CALL_EXPR_ORDERED_ARGS (t);
            bool rev = CALL_EXPR_REVERSE_ARGS (t);
-           bool thk = CALL_FROM_THUNK_P (t);
-           if (op || ord || rev || thk)
+           if (op || ord || rev)
              {
                function = extract_call_expr (ret);
                CALL_EXPR_OPERATOR_SYNTAX (function) = op;
                CALL_EXPR_ORDERED_ARGS (function) = ord;
                CALL_EXPR_REVERSE_ARGS (function) = rev;
-               if (thk)
-                 {
-                   if (TREE_CODE (function) == CALL_EXPR)
-                     CALL_FROM_THUNK_P (function) = true;
-                   else
-                     AGGR_INIT_FROM_THUNK_P (function) = true;
-                   /* The thunk location is not interesting.  */
-                   SET_EXPR_LOCATION (function, UNKNOWN_LOCATION);
-                 }
              }
          }
 
index ec722a360357d2a4d4aa5883a23928deb0d69401..7045284552975fd3c4ff9216f88d4b9974945a83 100644 (file)
@@ -9728,6 +9728,11 @@ check_return_expr (tree retval, bool *no_warning)
            }
        }
 
+      /* The call in a (lambda) thunk needs no conversions.  */
+      if (TREE_CODE (retval) == CALL_EXPR
+         && CALL_FROM_THUNK_P (retval))
+       converted = true;
+
       /* First convert the value to the function's return type, then
         to the type of return value's location to handle the
         case that functype is smaller than the valtype.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv13.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv13.C
new file mode 100644 (file)
index 0000000..159c4cc
--- /dev/null
@@ -0,0 +1,33 @@
+// PR c++/86943
+// { dg-do run { target c++14 } }
+
+int c[3];
+
+struct S
+{
+  S () : s (1234) { c[0]++; }
+  S (const S &) { __builtin_abort (); }
+  S (S &&x) noexcept { if (x.s != 1234) __builtin_abort (); s = 1234; x.s = 2345; c[1]++; }
+  ~S () { if (s != 1234 && s != 2345) __builtin_abort (); c[2]++; }
+  int s;
+};
+
+using F = S (*) (S);
+
+F
+foo ()
+{
+  return [] (auto val)->S { if (val.s != 1234) __builtin_abort (); return {}; };
+}
+
+int
+main ()
+{
+  {
+    volatile F f = foo ();
+    S s = f ({});
+    if (s.s != 1234) __builtin_abort ();
+  }
+  if (c[0] + c[1] != c[2])
+    __builtin_abort ();
+}