diff mbox

PR c++/66735 lambda capture by reference

Message ID 0b6654de-8dbe-0e93-cc9e-771c08e6325b@acm.org
State New
Headers show

Commit Message

Nathan Sidwell Jan. 4, 2017, 1:52 p.m. UTC
On 01/04/2017 12:36 AM, Jason Merrill wrote:
> On 01/03/2017 08:57 AM, Nathan Sidwell wrote:


>> +      type = auto_node;

>> +      if (by_reference_p)


>> +      type = build_reference_type (type);

>> +      by_reference_p = false;

^^^ here
>> +  if (!is_this && by_reference_p)

>> +    type = build_reference_type (type);

>

> This looks like it will call build_reference_type twice in the explicit

> init case, producing a reference to reference.


Note the clearing of by_reference_p just above in this case, but I admit 
that's confusing.  Anyway, it's rendered moot by this patch which addresses:

>>      if (DECLTYPE_FOR_LAMBDA_CAPTURE (t))

>>        type = lambda_capture_field_type (type,

>> -                        DECLTYPE_FOR_INIT_CAPTURE (t));

>> +                        DECLTYPE_FOR_INIT_CAPTURE (t),

>> +                        /*by_reference_p=*/false);

>

> Always passing false seems unlikely to be correct.


I wondered about that, but obviously failed to come up with a testcase 
to expose it.  This patch adds such a case, and exposes the need to add 
another flag to DECLTYPE to indicate reference capture -- rather than 
wrap the dependent capture in a reference type itself, as was previously 
the case.

ok?

nathan

-- 
Nathan Sidwell

Comments

Jason Merrill Jan. 4, 2017, 3:15 p.m. UTC | #1
OK.

On Wed, Jan 4, 2017 at 8:52 AM, Nathan Sidwell <nathan@acm.org> wrote:
> On 01/04/2017 12:36 AM, Jason Merrill wrote:

>>

>> On 01/03/2017 08:57 AM, Nathan Sidwell wrote:

>

>

>>> +      type = auto_node;

>>> +      if (by_reference_p)

>

>

>>> +      type = build_reference_type (type);

>>> +      by_reference_p = false;

>

> ^^^ here

>>>

>>> +  if (!is_this && by_reference_p)

>>> +    type = build_reference_type (type);

>>

>>

>> This looks like it will call build_reference_type twice in the explicit

>> init case, producing a reference to reference.

>

>

> Note the clearing of by_reference_p just above in this case, but I admit

> that's confusing.  Anyway, it's rendered moot by this patch which addresses:

>

>>>      if (DECLTYPE_FOR_LAMBDA_CAPTURE (t))

>>>        type = lambda_capture_field_type (type,

>>> -                        DECLTYPE_FOR_INIT_CAPTURE (t));

>>> +                        DECLTYPE_FOR_INIT_CAPTURE (t),

>>> +                        /*by_reference_p=*/false);

>>

>>

>> Always passing false seems unlikely to be correct.

>

>

> I wondered about that, but obviously failed to come up with a testcase to

> expose it.  This patch adds such a case, and exposes the need to add another

> flag to DECLTYPE to indicate reference capture -- rather than wrap the

> dependent capture in a reference type itself, as was previously the case.

>

>

> ok?

>

> nathan

>

> --

> Nathan Sidwell
diff mbox

Patch

2017-01-04  Nathan Sidwell  <nathan@acm.org>

	cp/
	PR c++/66735
	* cp-tree.h (DECLTYPE_FOR_REF_CAPTURE): New.
	(lambda_capture_field_type): Update prototype.
	* lambda.c (lambda_capture_field_type): Add is_reference parm.
	Add referenceness here.
	(add_capture): Adjust lambda_capture_field_type call, refactor
	error checking.
	* pt.c (tsubst): Adjust lambda_capture_field_type call.

	testsuite/
	PR c++/66735
	* g++.dg/cpp1y/pr66735.C: New.

Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 244054)
+++ cp/cp-tree.h	(working copy)
@@ -181,6 +181,7 @@  operator == (const cp_expr &lhs, tree rh
       BIND_EXPR_BODY_BLOCK (in BIND_EXPR)
       DECL_NON_TRIVIALLY_INITIALIZED_P (in VAR_DECL)
       CALL_EXPR_ORDERED_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
+      DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE)
    4: TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
 	  CALL_EXPR, or FIELD_DECL).
       IDENTIFIER_TYPENAME_P (in IDENTIFIER_NODE)
@@ -4103,6 +4104,8 @@  more_aggr_init_expr_args_p (const aggr_i
   TREE_LANG_FLAG_1 (DECLTYPE_TYPE_CHECK (NODE))
 #define DECLTYPE_FOR_LAMBDA_PROXY(NODE) \
   TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
+#define DECLTYPE_FOR_REF_CAPTURE(NODE) \
+  TREE_LANG_FLAG_3 (DECLTYPE_TYPE_CHECK (NODE))
 
 /* Nonzero for VAR_DECL and FUNCTION_DECL node means that `extern' was
    specified in its declaration.  This can also be set for an
@@ -6528,7 +6531,7 @@  extern tree finish_trait_expr			(enum cp
 extern tree build_lambda_expr                   (void);
 extern tree build_lambda_object			(tree);
 extern tree begin_lambda_type                   (tree);
-extern tree lambda_capture_field_type		(tree, bool);
+extern tree lambda_capture_field_type		(tree, bool, bool);
 extern tree lambda_return_type			(tree);
 extern tree lambda_proxy_type			(tree);
 extern tree lambda_function			(tree);
Index: cp/lambda.c
===================================================================
--- cp/lambda.c	(revision 244054)
+++ cp/lambda.c	(working copy)
@@ -211,29 +211,45 @@  lambda_function (tree lambda)
 }
 
 /* Returns the type to use for the FIELD_DECL corresponding to the
-   capture of EXPR.
-   The caller should add REFERENCE_TYPE for capture by reference.  */
+   capture of EXPR.  EXPLICIT_INIT_P indicates whether this is a
+   C++14 init capture, and BY_REFERENCE_P indicates whether we're
+   capturing by reference.  */
 
 tree
-lambda_capture_field_type (tree expr, bool explicit_init_p)
+lambda_capture_field_type (tree expr, bool explicit_init_p,
+			   bool by_reference_p)
 {
   tree type;
   bool is_this = is_this_parameter (tree_strip_nop_conversions (expr));
+
   if (!is_this && type_dependent_expression_p (expr))
     {
       type = cxx_make_type (DECLTYPE_TYPE);
       DECLTYPE_TYPE_EXPR (type) = expr;
       DECLTYPE_FOR_LAMBDA_CAPTURE (type) = true;
       DECLTYPE_FOR_INIT_CAPTURE (type) = explicit_init_p;
+      DECLTYPE_FOR_REF_CAPTURE (type) = by_reference_p;
       SET_TYPE_STRUCTURAL_EQUALITY (type);
     }
   else if (!is_this && explicit_init_p)
     {
-      type = make_auto ();
-      type = do_auto_deduction (type, expr, type);
+      tree auto_node = make_auto ();
+      
+      type = auto_node;
+      if (by_reference_p)
+	/* Add the reference now, so deduction doesn't lose
+	   outermost CV qualifiers of EXPR.  */
+	type = build_reference_type (type);
+      type = do_auto_deduction (type, expr, auto_node);
     }
   else
-    type = non_reference (unlowered_expr_type (expr));
+    {
+      type = non_reference (unlowered_expr_type (expr));
+
+      if (!is_this && by_reference_p)
+	type = build_reference_type (type);
+    }
+
   return type;
 }
 
@@ -504,9 +520,11 @@  add_capture (tree lambda, tree id, tree
     }
   else
     {
-      type = lambda_capture_field_type (initializer, explicit_init_p);
+      type = lambda_capture_field_type (initializer, explicit_init_p,
+					by_reference_p);
       if (type == error_mark_node)
 	return error_mark_node;
+
       if (id == this_identifier && !by_reference_p)
 	{
 	  gcc_assert (POINTER_TYPE_P (type));
@@ -514,17 +532,19 @@  add_capture (tree lambda, tree id, tree
 	  initializer = cp_build_indirect_ref (initializer, RO_NULL,
 					       tf_warning_or_error);
 	}
-      if (id != this_identifier && by_reference_p)
+
+      if (dependent_type_p (type))
+	;
+      else if (id != this_identifier && by_reference_p)
 	{
-	  type = build_reference_type (type);
-	  if (!dependent_type_p (type) && !lvalue_p (initializer))
+	  if (!lvalue_p (initializer))
 	    error ("cannot capture %qE by reference", initializer);
 	}
       else
 	{
 	  /* Capture by copy requires a complete type.  */
 	  type = complete_type (type);
-	  if (!dependent_type_p (type) && !COMPLETE_TYPE_P (type))
+	  if (!COMPLETE_TYPE_P (type))
 	    {
 	      error ("capture by copy of incomplete type %qT", type);
 	      cxx_incomplete_type_inform (type);
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 244054)
+++ cp/pt.c	(working copy)
@@ -13988,7 +13988,8 @@  tsubst (tree t, tree args, tsubst_flags_
 
 	if (DECLTYPE_FOR_LAMBDA_CAPTURE (t))
 	  type = lambda_capture_field_type (type,
-					    DECLTYPE_FOR_INIT_CAPTURE (t));
+					    DECLTYPE_FOR_INIT_CAPTURE (t),
+					    DECLTYPE_FOR_REF_CAPTURE (t));
 	else if (DECLTYPE_FOR_LAMBDA_PROXY (t))
 	  type = lambda_proxy_type (type);
 	else
Index: testsuite/g++.dg/cpp1y/pr66735.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr66735.C	(revision 0)
+++ testsuite/g++.dg/cpp1y/pr66735.C	(working copy)
@@ -0,0 +1,22 @@ 
+// { dg-do compile { target c++14 } }
+
+// PR c++/66735, lost constness on reference capture
+
+template <typename T> void Foo ()
+{
+  T const x = 5;
+
+  auto l = [&rx = x]() {};
+
+  l ();
+}
+
+void Baz ()
+{
+  int const x = 5;
+  auto l = [&rx = x]() {};
+
+
+  l ();
+  Foo<int> ();
+}