diff mbox

[C++] PR 61088

Message ID 537DB4F9.2060605@oracle.com
State New
Headers show

Commit Message

Paolo Carlini May 22, 2014, 8:27 a.m. UTC
Hi,

again, a simple core issue, an ICE on invalid, with some rather more 
interesting side issues. The core is that we are not enforcing that an 
incomplete type cannot be captured by value. Thus the add_capture check. 
Then:
1- I'm also adding the early error_mark_node check because otherwise in 
some cases we ICE during error recovery when COMPLETE_TYPE_P gets an 
error_mark_node. I added the second function g() in the testcase to 
cover that.
2- I wish I could just do type = error_mark_node, for the by value 
capture error, like we do above for the VLA-related error, instead of 
returning error_mark_node, but that would result in a suboptimal 
diagnostic for the existing lambda-ice7.C. For it, we used to produce:

lambda-ice7.C: In function ‘void foo(A&)’:
lambda-ice7.C:8:3: error: invalid use of incomplete type ‘struct A’
[=](){a;};
^
lambda-ice7.C:4:8: error: forward declaration of ‘struct A’
struct A;
^
lambda-ice7.C:8:3: error: invalid use of incomplete type ‘struct A’
[=](){a;};
^
lambda-ice7.C:4:8: error: forward declaration of ‘struct A’
struct A;
^

and, after the patch:

lambda-ice7.C: In lambda function:
lambda-ice7.C:8:9: error: cannot capture by value ‘a’ of incomplete type ‘A’
[=](){a;};
^
lambda-ice7.C:4:8: note: forward declaration of ‘struct A’
struct A;
^

which I think is nearly optimal. With type = error_mark_node we would 
only add an additional error to the former.

3- The cxx_incomplete_type_inform check is because in some cases 
TYPE_MAIN_DECL can indeed be null (traditionally we used + which means 
location_of, which handles that). This happens for example for f() in 
the new testcase, and we don't emit any inform for it (at variance with 
lambda-ice7.C, for example). I think it's fine.

4- Finally, something I noticed while working on add_capture: right 
above the new check there is:

type = lambda_capture_field_type (initializer, explicit_init_p);
if (by_reference_p)
{
type = build_reference_type (type);
if (!real_lvalue_p (initializer))
error ("cannot capture %qE by reference", initializer);
}

now, interestingly, nothing in the testsuite exercises this error. And, 
so far, I failed to create a testcase for it. The Standard too doesn't 
seem to me so clear about that. Ideas?!?

Thanks!
Paolo.

/////////////////////////////
/cp
2014-05-22  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/61088
	* lambda.c (add_capture): Enforce that capture by value requires
	complete type.
	* typeck2.c (cxx_incomplete_type_inform): Early return if
	TYPE_MAIN_DECL is null.

/testsuite
2014-05-22  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/61088
	* g++.dg/cpp0x/lambda/lambda-ice13.C: New.
	* g++.dg/cpp0x/lambda/lambda-ice7.C: Adjust.

Comments

Jason Merrill May 22, 2014, 6:13 p.m. UTC | #1
On 05/22/2014 04:27 AM, Paolo Carlini wrote:
> lambda-ice7.C:8:9: error: cannot capture by value ‘a’ of incomplete type
> ‘A’
> [=](){a;};
> ^

All the carets in your mail are in the first column; is this one in the 
right place for you?

Let's not print out the expression, we've been moving away from that. 
Maybe "capture by value of incomplete type 'A'".

OK with that change.

> 4- Finally, something I noticed while working on add_capture: right above the new check there is:
>
> type = lambda_capture_field_type (initializer, explicit_init_p);
> if (by_reference_p)
> {
> type = build_reference_type (type);
> if (!real_lvalue_p (initializer))
> error ("cannot capture %qE by reference", initializer);
> }
>
> now, interestingly, nothing in the testsuite exercises this error. And, so far, I failed to create a testcase for it. The Standard too doesn't seem to me so clear about that. Ideas?!?

void f()
{
   [&x=1]{}
}

Jason
Paolo Carlini May 22, 2014, 6:26 p.m. UTC | #2
Hi,

On 05/22/2014 08:13 PM, Jason Merrill wrote:
> On 05/22/2014 04:27 AM, Paolo Carlini wrote:
>> lambda-ice7.C:8:9: error: cannot capture by value ‘a’ of incomplete type
>> ‘A’
>> [=](){a;};
>> ^
>
> All the carets in your mail are in the first column; is this one in 
> the right place for you?
It would be definitely wrong, but it's just that I did something wrong 
with the mailer, it shows Ok on my shell, under the a. Look at the 
column, 9, which is fine.
> Let's not print out the expression, we've been moving away from that. 
> Maybe "capture by value of incomplete type 'A'".
Good. I wanted to ask about that. Also, by copy instead of by value, 
right? Because the Standard always talks about copy (likewise clang).
>
> OK with that change.
>
>> 4- Finally, something I noticed while working on add_capture: right 
>> above the new check there is:
>>
>> type = lambda_capture_field_type (initializer, explicit_init_p);
>> if (by_reference_p)
>> {
>> type = build_reference_type (type);
>> if (!real_lvalue_p (initializer))
>> error ("cannot capture %qE by reference", initializer);
>> }
>>
>> now, interestingly, nothing in the testsuite exercises this error. 
>> And, so far, I failed to create a testcase for it. The Standard too 
>> doesn't seem to me so clear about that. Ideas?!?
>
> void f()
> {
>   [&x=1]{}
> }
Right, thanks. I'm probably going to add it, at some point. Me, I was 
looking for something not using C++14 initializers, I think in that case 
is more difficult!?!

Paolo.
Jason Merrill May 22, 2014, 7:08 p.m. UTC | #3
On 05/22/2014 02:26 PM, Paolo Carlini wrote:
> Good. I wanted to ask about that. Also, by copy instead of by value,
> right? Because the Standard always talks about copy (likewise clang).

Yes.

> Right, thanks. I'm probably going to add it, at some point. Me, I was looking for something not using C++14 initializers, I think in that case is more difficult!?!

The error can only occur with a C++14 initializer; a (captured) variable 
is always an lvalue.

Jason
diff mbox

Patch

Index: cp/lambda.c
===================================================================
--- cp/lambda.c	(revision 210682)
+++ cp/lambda.c	(working copy)
@@ -456,6 +456,9 @@  add_capture (tree lambda, tree id, tree orig_init,
     initializer = build_x_compound_expr_from_list (initializer, ELK_INIT,
 						   tf_warning_or_error);
   type = TREE_TYPE (initializer);
+  if (type == error_mark_node)
+    return error_mark_node;
+
   if (array_of_runtime_bound_p (type))
     {
       vla = true;
@@ -492,8 +495,17 @@  add_capture (tree lambda, tree id, tree orig_init,
 	    error ("cannot capture %qE by reference", initializer);
 	}
       else
-	/* Capture by copy requires a complete type.  */
-	type = complete_type (type);
+	{
+	  /* Capture by copy requires a complete type.  */
+	  type = complete_type (type);
+	  if (!dependent_type_p (type) && !COMPLETE_TYPE_P (type))
+	    {
+	      error ("cannot capture by value %qE of incomplete "
+		     "type %qT", initializer, type);
+	      cxx_incomplete_type_inform (type);
+	      return error_mark_node;
+	    }
+	}
     }
 
   /* Add __ to the beginning of the field name so that user code
Index: cp/typeck2.c
===================================================================
--- cp/typeck2.c	(revision 210682)
+++ cp/typeck2.c	(working copy)
@@ -434,6 +434,9 @@  abstract_virtuals_error (abstract_class_use use, t
 void
 cxx_incomplete_type_inform (const_tree type)
 {
+  if (!TYPE_MAIN_DECL (type))
+    return;
+
   location_t loc = DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type));
   tree ptype = strip_top_quals (CONST_CAST_TREE (type));
 
Index: testsuite/g++.dg/cpp0x/lambda/lambda-ice13.C
===================================================================
--- testsuite/g++.dg/cpp0x/lambda/lambda-ice13.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/lambda/lambda-ice13.C	(working copy)
@@ -0,0 +1,14 @@ 
+// PR c++/61088
+// { dg-do compile { target c++11 } }
+
+void f()
+{
+  typedef void (*X) ();
+  X x[] = { [x](){} };  // { dg-error "incomplete type" }
+}
+
+void g()
+{
+  typedef void (X) ();
+  X x[] = { [x](){} };  // { dg-error "array of functions|not declared" }
+}
Index: testsuite/g++.dg/cpp0x/lambda/lambda-ice7.C
===================================================================
--- testsuite/g++.dg/cpp0x/lambda/lambda-ice7.C	(revision 210682)
+++ testsuite/g++.dg/cpp0x/lambda/lambda-ice7.C	(working copy)
@@ -5,5 +5,5 @@  struct A;         // { dg-message "forward declara
 
 void foo(A& a)
 {
-  [=](){a;};      // { dg-error "invalid use of incomplete type" }
+  [=](){a;};      // { dg-error "incomplete type" }
 }