[PR,c++/83406] deducing lambda type

Message ID 95555f6b-4822-da15-7ceb-717007a3b8ba@acm.org
State New
Headers show
Series
  • [PR,c++/83406] deducing lambda type
Related show

Commit Message

Nathan Sidwell Dec. 21, 2017, 5:33 p.m.
Jason, this needs a sanity check

83406 was exposed by Jason's recent patch removing return_type & closure 
from the tree_lambda_expr node:
Remove unnecessary LAMBDA_EXPR fields.

	* cp-tree.h (LAMBDA_EXPR_CLOSURE): Use TREE_TYPE.
	(LAMBDA_EXPR_RETURN_TYPE): Remove.
	(struct tree_lambda_expr): Remove closure and return_type fields.
	* lambda.c (build_lambda_expr): Don't set LAMBDA_EXPR_RETURN_TYPE.
	* pt.c (tsubst_copy_and_build): Likewise.
	* parser.c (cp_parser_lambda_declarator_opt): Track return type.
	(cp_parser_lambda_body): Adjust unspecified return type check.
	* ptree.c (cxx_print_lambda_node): Don't print closure or
	return type.

The culprit is cp_parser_lambda_body
    if (is_auto (TREE_TYPE (TREE_TYPE (fco))) ...

is_auto is true for []() {...}, which is what we want and things like [] 
()->decltype(auto) {...}, which we don't.  The simple fix is to check 
TYPE_HAS_LATE_RETURN_TYPE instead, as that's only false in the former 
case.  However, this whole if looks bogus.

This quoted text is not in the std (even adjusting by +3 for the clause 
renumbering).  It's not in the c++11 draft I have available either.
>     /* 5.1.1.4 of the standard says:

>          If a lambda-expression does not include a trailing-return-type, it

>          is as if the trailing-return-type denotes the following type:

> 	  * if the compound-statement is of the form

>                { return attribute-specifier [opt] expression ; }

>              the type of the returned expression after lvalue-to-rvalue

>              conversion (_conv.lval_ 4.1), array-to-pointer conversion

>              (_conv.array_ 4.2), and function-to-pointer conversion

>              (_conv.func_ 4.3);

>           * otherwise, void.  */


And the next comment is confusing, the second sentence appears to be a 
total lie.
>     /* In a lambda that has neither a lambda-return-type-clause

>        nor a deducible form, errors should be reported for return statements

>        in the body.  Since we used void as the placeholder return type, parsing

>        the body as usual will give such desired behavior.  */


Removing it breaks nothing beyond changing the diagnostic that 
g++.dg/cpp0x/lambda/lambda-ice15.C expects.  And fixes this regression.

The introductory comment:
>   /* Finish the function call operator

>      - class_specifier

>      + late_parsing_for_member

>      + function_definition_after_declarator

>      + ctor_initializer_opt_and_function_body  */

looks like the kind of development note I'd write to myself.  It doesn't 
match the code (any more?).

Jason, have I missed something?

nathan

-- 
Nathan Sidwell

Comments

Jason Merrill Dec. 21, 2017, 6:46 p.m. | #1
On Thu, Dec 21, 2017 at 12:33 PM, Nathan Sidwell <nathan@acm.org> wrote:
> Jason, this needs a sanity check

>

> 83406 was exposed by Jason's recent patch removing return_type & closure

> from the tree_lambda_expr node:

> Remove unnecessary LAMBDA_EXPR fields.

>

>         * cp-tree.h (LAMBDA_EXPR_CLOSURE): Use TREE_TYPE.

>         (LAMBDA_EXPR_RETURN_TYPE): Remove.

>         (struct tree_lambda_expr): Remove closure and return_type fields.

>         * lambda.c (build_lambda_expr): Don't set LAMBDA_EXPR_RETURN_TYPE.

>         * pt.c (tsubst_copy_and_build): Likewise.

>         * parser.c (cp_parser_lambda_declarator_opt): Track return type.

>         (cp_parser_lambda_body): Adjust unspecified return type check.

>         * ptree.c (cxx_print_lambda_node): Don't print closure or

>         return type.

>

> The culprit is cp_parser_lambda_body

>    if (is_auto (TREE_TYPE (TREE_TYPE (fco))) ...

>

> is_auto is true for []() {...}, which is what we want and things like []

> ()->decltype(auto) {...}, which we don't.  The simple fix is to check

> TYPE_HAS_LATE_RETURN_TYPE instead, as that's only false in the former case.


Ah, true.

> However, this whole if looks bogus.

>

> This quoted text is not in the std (even adjusting by +3 for the clause

> renumbering).  It's not in the c++11 draft I have available either.

>>

>>     /* 5.1.1.4 of the standard says:

>>          If a lambda-expression does not include a trailing-return-type, it

>>          is as if the trailing-return-type denotes the following type:

>>           * if the compound-statement is of the form

>>                { return attribute-specifier [opt] expression ; }

>>              the type of the returned expression after lvalue-to-rvalue

>>              conversion (_conv.lval_ 4.1), array-to-pointer conversion

>>              (_conv.array_ 4.2), and function-to-pointer conversion

>>              (_conv.func_ 4.3);

>>           * otherwise, void.  */


It's in published C++11 (N3290), but...

> And the next comment is confusing, the second sentence appears to be a total

> lie.

>>

>>     /* In a lambda that has neither a lambda-return-type-clause

>>        nor a deducible form, errors should be reported for return statements

>>        in the body.  Since we used void as the placeholder return type, parsing

>>        the body as usual will give such desired behavior.  */


Not a lie for original C++11, given the above text.  But none of my
compilers diagnose this, probably because we decided to consider the
lambda changes in N3638 to be a C++11 DR.

> Removing it breaks nothing beyond changing the diagnostic that

> g++.dg/cpp0x/lambda/lambda-ice15.C expects.  And fixes this regression.


OK.  Let's replace it with a comment about N3638.

> The introductory comment:

>>

>>   /* Finish the function call operator

>>      - class_specifier

>>      + late_parsing_for_member

>>      + function_definition_after_declarator

>>      + ctor_initializer_opt_and_function_body  */

>

> looks like the kind of development note I'd write to myself.  It doesn't

> match the code (any more?).


I think the comment was meant to explain that the code below was doing
the pieces that would normally be handled by those functions.  I don't
mind removing it.

Jason

Patch

2017-12-21  Nathan Sidwell  <nathan@acm.org>

	PR c++/83406
	* parser.c (cp_parser_lambda_body): Remove obsolete
	single-return-statement handling.

	PR c++/83406
	* g++.dg/cpp0x/lambda/lambda-ice15.C: Adjust error.
	* g++.dg/cpp1y/pr83406.C: New.

Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 255940)
+++ gcc/cp/parser.c	(working copy)
@@ -10578,98 +10578,38 @@  cp_parser_lambda_body (cp_parser* parser
   bool nested = (current_function_decl != NULL_TREE);
   bool local_variables_forbidden_p = parser->local_variables_forbidden_p;
   bool in_function_body = parser->in_function_body;
+
   if (nested)
     push_function_context ();
   else
     /* Still increment function_depth so that we don't GC in the
        middle of an expression.  */
     ++function_depth;
+
   vec<tree> omp_privatization_save;
   save_omp_privatization_clauses (omp_privatization_save);
   /* Clear this in case we're in the middle of a default argument.  */
   parser->local_variables_forbidden_p = false;
   parser->in_function_body = true;
 
-  /* Finish the function call operator
-     - class_specifier
-     + late_parsing_for_member
-     + function_definition_after_declarator
-     + ctor_initializer_opt_and_function_body  */
   {
     local_specialization_stack s (lss_copy);
-
     tree fco = lambda_function (lambda_expr);
     tree body = start_lambda_function (fco, lambda_expr);
-    bool done = false;
-    tree compound_stmt;
-
     matching_braces braces;
-    if (!braces.require_open (parser))
-      goto out;
-
-    compound_stmt = begin_compound_stmt (0);
 
-    /* 5.1.1.4 of the standard says:
-         If a lambda-expression does not include a trailing-return-type, it
-         is as if the trailing-return-type denotes the following type:
-	  * if the compound-statement is of the form
-               { return attribute-specifier [opt] expression ; }
-             the type of the returned expression after lvalue-to-rvalue
-             conversion (_conv.lval_ 4.1), array-to-pointer conversion
-             (_conv.array_ 4.2), and function-to-pointer conversion
-             (_conv.func_ 4.3);
-          * otherwise, void.  */
-
-    /* In a lambda that has neither a lambda-return-type-clause
-       nor a deducible form, errors should be reported for return statements
-       in the body.  Since we used void as the placeholder return type, parsing
-       the body as usual will give such desired behavior.  */
-    if (is_auto (TREE_TYPE (TREE_TYPE (fco)))
-        && cp_lexer_peek_nth_token (parser->lexer, 1)->keyword == RID_RETURN
-        && cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SEMICOLON)
+    if (braces.require_open (parser))
       {
-	tree expr = NULL_TREE;
-	cp_id_kind idk = CP_ID_KIND_NONE;
+	tree compound_stmt = begin_compound_stmt (0);
 
-	/* Parse tentatively in case there's more after the initial return
-	   statement.  */
-	cp_parser_parse_tentatively (parser);
-
-	cp_parser_require_keyword (parser, RID_RETURN, RT_RETURN);
-
-	expr = cp_parser_expression (parser, &idk);
-
-	cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
-	braces.require_close (parser);
-
-	if (cp_parser_parse_definitely (parser))
-	  {
-	    if (!processing_template_decl)
-	      {
-		tree type = lambda_return_type (expr);
-		apply_deduced_return_type (fco, type);
-		if (type == error_mark_node)
-		  expr = error_mark_node;
-	      }
-
-	    /* Will get error here if type not deduced yet.  */
-	    finish_return_stmt (expr);
-
-	    done = true;
-	  }
-      }
-
-    if (!done)
-      {
 	while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
 	  cp_parser_label_declaration (parser);
 	cp_parser_statement_seq_opt (parser, NULL_TREE);
 	braces.require_close (parser);
-      }
 
-    finish_compound_stmt (compound_stmt);
+	finish_compound_stmt (compound_stmt);
+      }
 
-  out:
     finish_lambda_function (body);
   }
 
Index: gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice15.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice15.C	(revision 255940)
+++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice15.C	(working copy)
@@ -5,6 +5,11 @@  class A
 {
   void foo ()
   {
-    [=] { return foo; };  // { dg-error "invalid use of member function" }
+    [=] { return foo; };  // { dg-error "cannot convert" }
+  }
+  void bar () const;
+  void bar ()
+  {
+    [=] { return bar; };  // { dg-error "unable to deduce" }
   }
 };
Index: gcc/testsuite/g++.dg/cpp1y/pr83406.C
===================================================================
--- gcc/testsuite/g++.dg/cpp1y/pr83406.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp1y/pr83406.C	(working copy)
@@ -0,0 +1,41 @@ 
+// { dg-do compile { target c++14 } }
+// PR 83406, lambda late returns are not the same as missing returns
+
+class Bar
+{
+public:
+  const int& getter() const;
+  int& getter();
+};
+
+auto one = [](const Bar& bar) -> decltype(auto)
+{
+  return bar.getter();
+};
+
+auto two = [](const Bar& bar) -> auto
+{
+  return bar.getter();
+};
+
+auto three = [](const Bar& bar)
+{
+  return bar.getter();
+};
+
+template <typename T, typename U> struct X 
+{
+  static const bool same = false;
+};
+
+template <typename T> struct X<T,T>
+{
+  static const bool same = true;
+};
+
+void frob (Bar &x)
+{
+  static_assert (X<const int &, decltype (one (x))>::same, "not const int &");
+  static_assert (X<int, decltype (two (x))>::same, "not int");
+  static_assert (X<int, decltype (three (x))>::same, "not int");
+}