PR c++/86943 - wrong code converting lambda to function pointer.

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
This commit is contained in:
Jason Merrill 2019-01-29 21:43:04 -05:00 committed by Jason Merrill
parent dca2770bc0
commit 538a530848
5 changed files with 111 additions and 23 deletions

View 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]].

View 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;
}

View 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);
}
}
}

View 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. */

View File

@ -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 ();
}