This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PR c++/83406] deducing lambda type
- From: Nathan Sidwell <nathan at acm dot org>
- To: Jason Merrill <jason at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 21 Dec 2017 12:33:23 -0500
- Subject: [PR c++/83406] deducing lambda type
- Authentication-results: sourceware.org; auth=none
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");
+}