Jason,
this fixes 83160, where we complain about not having an lvalue in things
like:
void foo () {
const int a = 0;
[&a] () {
const int &b = a; // here
};
}
The problem is that we in convert_like_real we have ref_bind->identity
conversions, and the identity conversion calls 'mark_rvalue_use'. For a
regular rvalue use of a 'const int' VAR_DECL, we return the VAR_DECL --
not collapse it to the initialized value. However, for captures, there
is code to look through the capture when rvalue_p is true. We end up
looking through the lambda capture, through the captured 'a' and to its
initializer. oops.
Calling mark_lvalue_use instead is also wrong, because the identity conv
may of course be being applied to an rvalue, and we end up giving an
equally wrong error in the opposite case. Dealing with ck_identity this
way sees wrong -- context determines whether this is an rvalue or lvalue
use.
I think the solution is for the identity conv to know whether it's going
to be the subject of a direct reference binding and call mark_lvalue_use
in tha case. There's 2 issues with that:
1) mark_lvalue_use isn't quite right because we want to specify
reject_builtin to the inner mark_use call. (I didn't try changing
mark_lvalue_use to pass true, I suspected it'd break actual calls of
builtins). Fixed by making mark_use externally reachable, and passing
in an appropriate 'rvalue_p' parm directly. I think your recent patch
to select between the two marking fns may be neater using this entry point?
2) I modify direct_reference_binding to look at the incoming conv, and
if it is ck_identity set that conv's rvaluedness_matches_p flag. Then
deply that flag to determine the arg in #1. 'rvaluedness_matches_p'
seemed the least worst existing flag to press into service here.
WDYT?
nathan
--
Nathan Sidwell
2018-01-12 Nathan Sidwell <nat...@acm.org>
PR c++/83160
* cp-tree.h (mark_use): Declare.
* expr.c (mark_use): Make extern.
* call.c (direct_reference_binding): Set inner conv's
rvaluedness_matches_p, if it is an identity.
(convert_like_real): Mark lvalue or rvalue use for identity as
rvaledness_matches_p demands.
PR c++/83160
* g++.dg/cpp0x/pr83160.C: New.
Index: cp/call.c
===================================================================
--- cp/call.c (revision 256593)
+++ cp/call.c (working copy)
@@ -102,7 +102,8 @@ struct conversion {
being bound to an lvalue expression or an rvalue reference is
being bound to an rvalue expression. If KIND is ck_rvalue,
true when we are treating an lvalue as an rvalue (12.8p33). If
- KIND is ck_base, always false. */
+ KIND is ck_base, always false. If ck_identity, we will be
+ binding a reference directly. */
BOOL_BITFIELD rvaluedness_matches_p: 1;
BOOL_BITFIELD check_narrowing: 1;
/* The type of the expression resulting from the conversion. */
@@ -1501,6 +1502,10 @@ direct_reference_binding (tree type, con
That way, convert_like knows not to generate a temporary. */
conv->need_temporary_p = false;
}
+ else if (conv->kind == ck_identity)
+ /* Mark the identity conv as to not decay to rvalue. */
+ conv->rvaluedness_matches_p = true;
+
return build_conv (ck_ref_bind, type, conv);
}
@@ -6800,7 +6805,9 @@ convert_like_real (conversion *convs, tr
else
gcc_unreachable ();
}
- expr = mark_rvalue_use (expr);
+ expr = mark_use (expr, /*rvalue_p=*/!convs->rvaluedness_matches_p,
+ /*read_p=*/true, UNKNOWN_LOCATION,
+ /*reject_builtin=*/true);
if (type_unknown_p (expr))
expr = instantiate_type (totype, expr, complain);
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h (revision 256593)
+++ cp/cp-tree.h (working copy)
@@ -6328,6 +6328,9 @@ extern tree create_try_catch_expr
/* in expr.c */
extern tree cplus_expand_constant (tree);
+extern tree mark_use (tree expr, bool rvalue_p, bool read_p,
+ location_t = UNKNOWN_LOCATION,
+ bool reject_builtin = true);
extern tree mark_rvalue_use (tree,
location_t = UNKNOWN_LOCATION,
bool reject_builtin = true);
Index: cp/expr.c
===================================================================
--- cp/expr.c (revision 256593)
+++ cp/expr.c (working copy)
@@ -89,7 +89,7 @@ cplus_expand_constant (tree cst)
/* We've seen an actual use of EXPR. Possibly replace an outer variable
reference inside with its constant value or a lambda capture. */
-static tree
+tree
mark_use (tree expr, bool rvalue_p, bool read_p,
location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
Index: testsuite/g++.dg/cpp0x/pr83160.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr83160.C (revision 0)
+++ testsuite/g++.dg/cpp0x/pr83160.C (working copy)
@@ -0,0 +1,33 @@
+// { dg-do run { target c++11 } }
+// PR c++/83160 failed to capture as lvalue
+
+int main ()
+{
+ const int a = 0;
+
+ if (![&a] (const int *p)
+ {
+ const int &b = a;
+ // We should bind to the outer a
+ return &b == p;
+ } (&a))
+ return 1;
+
+ if (![&] (const int *p)
+ {
+ const int &b = a;
+ // We should bind to the outer a
+ return &b == p;
+ } (&a))
+ return 2;
+
+ if ([=] (const int *p)
+ {
+ const int &b = a;
+ // We should bind to the captured instance
+ return &b == p;
+ }(&a))
+ return 3;
+
+ return 0;
+}