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]

C++ PATCH for core issue 975 (extended lambda return type deduction)


At the Bloomington meeting last week the committee finally accepted my proposal to allow return type deduction from lambdas of arbitrary form as long as the deduced type is the same for all return statements, as implemented in G++. My implementation did the type comparison at template definition time, but others on the committee thought that the comparison should happen at instantiation time. This is indeed more user-friendly, and I went ahead and implemented the final resolution.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit f9451c5652862c301ecfe838fb335fccf03ed56a
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Aug 17 22:48:39 2011 -0400

    	Core 975
    	* decl.c (cxx_init_decl_processing): Initialize
    	dependent_lambda_return_type_node.
    	* cp-tree.h (cp_tree_index): Add CPTI_DEPENDENT_LAMBDA_RETURN_TYPE.
    	(dependent_lambda_return_type_node): Define.
    	(DECLTYPE_FOR_LAMBDA_RETURN): Remove.
    	* semantics.c (lambda_return_type): Handle overloaded function.
    	Use dependent_lambda_return_type_node instead of
    	DECLTYPE_FOR_LAMBDA_RETURN.
    	(apply_lambda_return_type): Don't check dependent_type_p.
    	* pt.c (tsubst_copy_and_build): Handle lambda return type deduction.
    	(instantiate_class_template_1): Likewise.
    	(tsubst): Don't use DECLTYPE_FOR_LAMBDA_RETURN.
    	* mangle.c (write_type): Likewise.
    	* typeck.c (structural_comptypes): Likewise.
    	(check_return_expr): Handle dependent_lambda_return_type_node.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ff5509e..8595943 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -83,7 +83,6 @@ c-common.h, not after.
       STMT_IS_FULL_EXPR_P (in _STMT)
       TARGET_EXPR_LIST_INIT_P (in TARGET_EXPR)
       LAMBDA_EXPR_MUTABLE_P (in LAMBDA_EXPR)
-      DECLTYPE_FOR_LAMBDA_RETURN (in DECLTYPE_TYPE)
       DECL_FINAL_P (in FUNCTION_DECL)
       QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF)
    2: IDENTIFIER_OPNAME_P (in IDENTIFIER_NODE)
@@ -775,6 +774,7 @@ enum cp_tree_index
     CPTI_CLASS_TYPE,
     CPTI_UNKNOWN_TYPE,
     CPTI_INIT_LIST_TYPE,
+    CPTI_DEPENDENT_LAMBDA_RETURN_TYPE,
     CPTI_VTBL_TYPE,
     CPTI_VTBL_PTR_TYPE,
     CPTI_STD,
@@ -846,6 +846,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
 #define class_type_node			cp_global_trees[CPTI_CLASS_TYPE]
 #define unknown_type_node		cp_global_trees[CPTI_UNKNOWN_TYPE]
 #define init_list_type_node		cp_global_trees[CPTI_INIT_LIST_TYPE]
+#define dependent_lambda_return_type_node cp_global_trees[CPTI_DEPENDENT_LAMBDA_RETURN_TYPE]
 #define vtbl_type_node			cp_global_trees[CPTI_VTBL_TYPE]
 #define vtbl_ptr_type_node		cp_global_trees[CPTI_VTBL_PTR_TYPE]
 #define std_node			cp_global_trees[CPTI_STD]
@@ -3425,12 +3426,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
   (DECLTYPE_TYPE_CHECK (NODE))->type_common.string_flag
 
 /* These flags indicate that we want different semantics from normal
-   decltype: lambda capture just drops references, lambda return also does
-   type decay, lambda proxies look through implicit dereference.  */
+   decltype: lambda capture just drops references, lambda proxies look
+   through implicit dereference.  */
 #define DECLTYPE_FOR_LAMBDA_CAPTURE(NODE) \
   TREE_LANG_FLAG_0 (DECLTYPE_TYPE_CHECK (NODE))
-#define DECLTYPE_FOR_LAMBDA_RETURN(NODE) \
-  TREE_LANG_FLAG_1 (DECLTYPE_TYPE_CHECK (NODE))
 #define DECLTYPE_FOR_LAMBDA_PROXY(NODE) \
   TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
 
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c125f05..c375cf7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -3597,6 +3597,10 @@ cxx_init_decl_processing (void)
   init_list_type_node = make_node (LANG_TYPE);
   record_unknown_type (init_list_type_node, "init list");
 
+  dependent_lambda_return_type_node = make_node (LANG_TYPE);
+  record_unknown_type (dependent_lambda_return_type_node,
+		       "undeduced lambda return type");
+
   {
     /* Make sure we get a unique function type, so we can give
        its pointer type a name.  (This wins for gdb.) */
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 53d4bc6..4c7cc79 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -1953,7 +1953,7 @@ write_type (tree type)
             case DECLTYPE_TYPE:
 	      /* These shouldn't make it into mangling.  */
 	      gcc_assert (!DECLTYPE_FOR_LAMBDA_CAPTURE (type)
-			  && !DECLTYPE_FOR_LAMBDA_RETURN (type));
+			  && !DECLTYPE_FOR_LAMBDA_PROXY (type));
 
 	      /* In ABI <5, we stripped decltype of a plain decl.  */
 	      if (!abi_version_at_least (5)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3f9a4c0..6b970f9 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8887,7 +8887,16 @@ instantiate_class_template_1 (tree type)
     }
 
   if (CLASSTYPE_LAMBDA_EXPR (type))
-    maybe_add_lambda_conv_op (type);
+    {
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (type);
+      if (LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (lambda))
+	{
+	  apply_lambda_return_type (lambda, void_type_node);
+	  LAMBDA_EXPR_RETURN_TYPE (lambda) = NULL_TREE;
+	}
+      instantiate_decl (lambda_function (type), false, false);
+      maybe_add_lambda_conv_op (type);
+    }
 
   /* Set the file and line number information to whatever is given for
      the class itself.  This puts error messages involving generated
@@ -11420,8 +11429,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
 	if (DECLTYPE_FOR_LAMBDA_CAPTURE (t))
 	  type = lambda_capture_field_type (type);
-	else if (DECLTYPE_FOR_LAMBDA_RETURN (t))
-	  type = lambda_return_type (type);
 	else if (DECLTYPE_FOR_LAMBDA_PROXY (t))
 	  type = lambda_proxy_type (type);
 	else
@@ -13882,10 +13889,17 @@ tsubst_copy_and_build (tree t,
 	LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t);
 	LAMBDA_EXPR_DISCRIMINATOR (r)
 	  = (LAMBDA_EXPR_DISCRIMINATOR (t));
-	LAMBDA_EXPR_CAPTURE_LIST (r)
-	  = RECUR (LAMBDA_EXPR_CAPTURE_LIST (t));
 	LAMBDA_EXPR_EXTRA_SCOPE (r)
 	  = RECUR (LAMBDA_EXPR_EXTRA_SCOPE (t));
+	if (LAMBDA_EXPR_RETURN_TYPE (t) == dependent_lambda_return_type_node)
+	  {
+	    LAMBDA_EXPR_RETURN_TYPE (r) = dependent_lambda_return_type_node;
+	    LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (r) = true;
+	  }
+	else
+	  LAMBDA_EXPR_RETURN_TYPE (r)
+	    = tsubst (LAMBDA_EXPR_RETURN_TYPE (t), args, complain, in_decl);
+
 	gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
 		    && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
 
@@ -13895,9 +13909,10 @@ tsubst_copy_and_build (tree t,
 	   declaration of the op() for later calls to lambda_function.  */
 	complete_type (type);
 
-	type = tsubst (LAMBDA_EXPR_RETURN_TYPE (t), args, complain, in_decl);
-	if (type)
-	  apply_lambda_return_type (r, type);
+	/* The capture list refers to closure members, so this needs to
+	   wait until after we finish instantiating the type.  */
+	LAMBDA_EXPR_CAPTURE_LIST (r)
+	  = RECUR (LAMBDA_EXPR_CAPTURE_LIST (t));
 
 	return build_lambda_object (r);
       }
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 2f62e35..5deb2eb 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -8323,18 +8323,14 @@ tree
 lambda_return_type (tree expr)
 {
   tree type;
-  if (BRACE_ENCLOSED_INITIALIZER_P (expr))
+  if (type_unknown_p (expr)
+      || BRACE_ENCLOSED_INITIALIZER_P (expr))
     {
-      warning (0, "cannot deduce lambda return type from a braced-init-list");
+      cxx_incomplete_type_error (expr, TREE_TYPE (expr));
       return void_type_node;
     }
   if (type_dependent_expression_p (expr))
-    {
-      type = cxx_make_type (DECLTYPE_TYPE);
-      DECLTYPE_TYPE_EXPR (type) = expr;
-      DECLTYPE_FOR_LAMBDA_RETURN (type) = true;
-      SET_TYPE_STRUCTURAL_EQUALITY (type);
-    }
+    type = dependent_lambda_return_type_node;
   else
     type = cv_unqualified (type_decays_to (unlowered_expr_type (expr)));
   return type;
@@ -8394,12 +8390,10 @@ apply_lambda_return_type (tree lambda, tree return_type)
 
   LAMBDA_EXPR_RETURN_TYPE (lambda) = return_type;
 
-  /* If we got a DECLTYPE_TYPE, don't stick it in the function yet,
-     it would interfere with instantiating the closure type.  */
-  if (dependent_type_p (return_type))
-    return;
   if (return_type == error_mark_node)
     return;
+  if (TREE_TYPE (TREE_TYPE (fco)) == return_type)
+    return;
 
   /* TREE_TYPE (FUNCTION_DECL) == METHOD_TYPE
      TREE_TYPE (METHOD_TYPE)   == return-type  */
@@ -8412,6 +8406,7 @@ apply_lambda_return_type (tree lambda, tree return_type)
   /* We already have a DECL_RESULT from start_preparsed_function.
      Now we need to redo the work it and allocate_struct_function
      did to reflect the new type.  */
+  gcc_assert (current_function_decl == fco);
   result = build_decl (input_location, RESULT_DECL, NULL_TREE,
 		       TYPE_MAIN_VARIANT (return_type));
   DECL_ARTIFICIAL (result) = 1;
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index a1f6761..ea42bfe 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1331,8 +1331,8 @@ structural_comptypes (tree t1, tree t2, int strict)
           != DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t2)
 	  || (DECLTYPE_FOR_LAMBDA_CAPTURE (t1)
 	      != DECLTYPE_FOR_LAMBDA_CAPTURE (t2))
-	  || (DECLTYPE_FOR_LAMBDA_RETURN (t1)
-	      != DECLTYPE_FOR_LAMBDA_RETURN (t2))
+	  || (DECLTYPE_FOR_LAMBDA_PROXY (t1)
+	      != DECLTYPE_FOR_LAMBDA_PROXY (t2))
           || !cp_tree_equal (DECLTYPE_TYPE_EXPR (t1), 
                              DECLTYPE_TYPE_EXPR (t2)))
         return false;
@@ -7655,15 +7655,14 @@ check_return_expr (tree retval, bool *no_warning)
 	  tree type = lambda_return_type (retval);
 	  tree oldtype = LAMBDA_EXPR_RETURN_TYPE (lambda);
 
-	  if (VOID_TYPE_P (type))
-	    { /* Nothing.  */ }
-	  else if (oldtype == NULL_TREE)
-	    {
-	      pedwarn (input_location, OPT_pedantic, "lambda return type "
-		       "can only be deduced when the return statement is "
-		       "the only statement in the function body");
-	      apply_lambda_return_type (lambda, type);
-	    }
+	  if (oldtype == NULL_TREE)
+	    apply_lambda_return_type (lambda, type);
+	  /* If one of the answers is type-dependent, we can't do any
+	     better until instantiation time.  */
+	  else if (oldtype == dependent_lambda_return_type_node)
+	    /* Leave it.  */;
+	  else if (type == dependent_lambda_return_type_node)
+	    apply_lambda_return_type (lambda, type);
 	  else if (!same_type_p (type, oldtype))
 	    error ("inconsistent types %qT and %qT deduced for "
 		   "lambda return type", type, oldtype);
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg.C
index bfe7aca..f93e78a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg.C
@@ -1,24 +1,22 @@
-// Testcase for an extension to allow return type deduction when the lambda
-// contains more than just a single return-statement.
+// Testcase for DR 975.
 
 // { dg-options -std=c++0x }
 
 bool b;
-template <class T>
-T f (T t)
-{
-  return [=]
-    {
-      auto i = t+1;
-      if (b)
-	return i+1;
-      else
-	return i+2;		// { dg-error "lambda return type" }
-    }();
+struct A { int fn1(); const int& fn2(); };
+struct B { int fn1(); long fn2(); };
+
+template <class T> int f (T t) {
+  return [](T t){
+    if (b)
+      return t.fn1();
+    else
+      return t.fn2();		// { dg-error "inconsistent types" }
+  }(t);
 }
 
 int main()
 {
-  if (f(1) != 3)
-    return 1;
+  f(A());			// { dg-bogus "" } int and const int& are compatible
+  f(B());			// { dg-message "from here" } int and long are not
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg2.C
index a236e6d..5c63409 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-ext-neg2.C
@@ -1,6 +1,5 @@
-// Test that in pedantic mode, we warn about the extension to allow return
-// type deduction when the lambda contains more than just a single
-// return-statement.
+// Test that this is accepted even when pedantic now that it's part
+// of the standard.
 
 // { dg-options "-std=c++0x -pedantic" }
 
@@ -11,7 +10,7 @@ T f (T t)
   [=] { return t+1; };		// OK
   return [=] {
     auto i = t+1;
-    return i+1;			// { dg-warning "only statement" }
+    return i+1;
   }();
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-neg.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-neg.C
index 4abdf59..c932c09 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-neg.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce-neg.C
@@ -1,5 +1,4 @@
 // { dg-options "-std=c++0x" }
-#include <cassert>
 
 int main() {
   int i = 0;
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce2.C
index 718d49c..eeb9814 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-deduce2.C
@@ -1,7 +1,11 @@
 // PR c++/43875
 // { dg-options "-std=c++0x" }
 
+void f();
+void f(int);
+
 int main()
 {
-   auto x2 = []{ return { 1, 2 }; }; // { dg-message "return" }
+  auto x1 = []{ return f; };	    // { dg-error "return|overloaded" }
+  auto x2 = []{ return { 1, 2 }; }; // { dg-error "return|list" }
 }

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