This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PR c++/83406] deducing lambda type


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
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");
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]