Patchwork C++ PATCH to implement N3638 changes to return type deduction

login
register
mail settings
Submitter Jason Merrill
Date April 19, 2013, 4:25 p.m.
Message ID <51716FF2.3060600@redhat.com>
Download mbox | patch
Permalink /patch/238049/
State New
Headers show

Comments

Jason Merrill - April 19, 2013, 4:25 p.m.
There were a few changes requested to my return type deduction paper 
during review in Bristol; this patch implements those changes.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit fd1e2aa05a014b59a800e5405fcb62eaef67e6f0
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Apr 15 22:27:33 2013 +0100

    	N3638 changes to return type deduction
    
    	* decl.c (undeduced_auto_decl): New.
    	(require_deduced_type): New.
    	(fndecl_declared_return_type): New.
    	(decls_match): Use it.
    	(duplicate_decls): Don't check for auto return.
    	(grokdeclarator): Reject virtual auto.
    	* class.c (resolve_address_of_overloaded_function): Handle
    	auto function templates.
    	* decl2.c (mark_used): Use undeduced_auto_decl, require_deduced_type.
    	* cp-tree.h: Declare new fns.
    	* error.c (dump_function_decl): Use fndecl_declared_return_type.
    	* search.c (check_final_overrider): Likewise.
    	* pt.c (make_decltype_auto): New.
    	(do_auto_deduction): Require plain decltype(auto).
    	(is_auto): Adjust.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 58248bf..b936ac8 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -7256,19 +7256,38 @@  resolve_address_of_overloaded_function (tree target_type,
 	       one, or vice versa.  */
 	    continue;
 
+	  tree ret = target_ret_type;
+
+	  /* If the template has a deduced return type, don't expose it to
+	     template argument deduction.  */
+	  if (undeduced_auto_decl (fn))
+	    ret = NULL_TREE;
+
 	  /* Try to do argument deduction.  */
 	  targs = make_tree_vec (DECL_NTPARMS (fn));
 	  instantiation = fn_type_unification (fn, explicit_targs, targs, args,
-					      nargs, target_ret_type,
+					       nargs, ret,
 					      DEDUCE_EXACT, LOOKUP_NORMAL,
 					       false, false);
 	  if (instantiation == error_mark_node)
 	    /* Instantiation failed.  */
 	    continue;
 
+	  /* And now force instantiation to do return type deduction.  */
+	  if (undeduced_auto_decl (instantiation))
+	    {
+	      ++function_depth;
+	      instantiate_decl (instantiation, /*defer*/false, /*class*/false);
+	      --function_depth;
+
+	      require_deduced_type (instantiation);
+	    }
+
 	  /* See if there's a match.  */
 	  if (same_type_p (target_fn_type, static_fn_type (instantiation)))
 	    matches = tree_cons (instantiation, fn, matches);
+
+	  ggc_free (targs);
 	}
 
       /* Now, remove all but the most specialized of the matches.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f7c65b6..a5c7548 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5225,6 +5225,9 @@  extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
 extern tree check_var_type			(tree, tree);
 extern tree reshape_init                        (tree, tree, tsubst_flags_t);
 extern tree next_initializable_field (tree);
+extern tree fndecl_declared_return_type		(tree);
+extern bool undeduced_auto_decl			(tree);
+extern void require_deduced_type		(tree);
 
 extern bool defer_mark_used_calls;
 extern GTY(()) vec<tree, va_gc> *deferred_mark_used_calls;
@@ -5425,6 +5428,7 @@  extern tree check_explicit_specialization	(tree, tree, int, int);
 extern int num_template_headers_for_class	(tree);
 extern void check_template_variable		(tree);
 extern tree make_auto				(void);
+extern tree make_decltype_auto			(void);
 extern tree do_auto_deduction			(tree, tree, tree);
 extern tree type_uses_auto			(tree);
 extern void append_type_to_template_for_access_check (tree, tree, tree,
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 3328812..b2f1c6e 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -992,10 +992,7 @@  decls_match (tree newdecl, tree olddecl)
 
       /* A declaration with deduced return type should use its pre-deduction
 	 type for declaration matching.  */
-      if (FNDECL_USED_AUTO (olddecl))
-	r2 = DECL_STRUCT_FUNCTION (olddecl)->language->x_auto_return_pattern;
-      else
-	r2 = TREE_TYPE (f2);
+      r2 = fndecl_declared_return_type (olddecl);
 
       if (same_type_p (TREE_TYPE (f1), r2))
 	{
@@ -1538,11 +1535,7 @@  duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 			      TYPE_ARG_TYPES (TREE_TYPE (olddecl))))
 	    {
 	      error ("new declaration %q#D", newdecl);
-	      if (FNDECL_USED_AUTO (olddecl))
-		error_at (DECL_SOURCE_LOCATION (olddecl), "ambiguates old "
-			  "declaration with deduced return type");
-	      else
-		error ("ambiguates old declaration %q+#D", olddecl);
+	      error ("ambiguates old declaration %q+#D", olddecl);
               return error_mark_node;
 	    }
 	  else
@@ -9503,6 +9496,9 @@  grokdeclarator (const cp_declarator *declarator,
 			  pedwarn (input_location, 0, "%qs function uses "
 				   "%<auto%> type specifier without trailing "
 				   "return type", name);
+			else if (virtualp)
+			  permerror (input_location, "virtual function cannot "
+				     "have deduced return type");
 		      }
 		    else if (!is_auto (type))
 		      {
@@ -14380,4 +14376,38 @@  cxx_comdat_group (tree decl)
   return name;
 }
 
+/* Returns the return type for FN as written by the user, which may include
+   a placeholder for a deduced return type.  */
+
+tree
+fndecl_declared_return_type (tree fn)
+{
+  fn = STRIP_TEMPLATE (fn);
+  if (FNDECL_USED_AUTO (fn))
+    return (DECL_STRUCT_FUNCTION (fn)->language
+	    ->x_auto_return_pattern);
+  else
+    return TREE_TYPE (TREE_TYPE (fn));
+}
+
+/* Returns true iff DECL was declared with an auto return type and it has
+   not yet been deduced to a real type.  */
+
+bool
+undeduced_auto_decl (tree decl)
+{
+  if (cxx_dialect < cxx1y)
+    return false;
+  return type_uses_auto (TREE_TYPE (decl));
+}
+
+/* Complain if DECL has an undeduced return type.  */
+
+void
+require_deduced_type (tree decl)
+{
+  if (undeduced_auto_decl (decl))
+    error ("use of %qD before deduction of %<auto%>", decl);
+}
+
 #include "gt-cp-decl.h"
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 1cc3e25..8d2385d 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4578,7 +4578,7 @@  mark_used (tree decl)
   if ((decl_maybe_constant_var_p (decl)
        || (TREE_CODE (decl) == FUNCTION_DECL
 	   && DECL_DECLARED_CONSTEXPR_P (decl))
-       || type_uses_auto (TREE_TYPE (decl)))
+       || undeduced_auto_decl (decl))
       && DECL_LANG_SPECIFIC (decl)
       && DECL_TEMPLATE_INFO (decl)
       && !uses_template_parms (DECL_TI_ARGS (decl)))
@@ -4601,11 +4601,7 @@  mark_used (tree decl)
       && uses_template_parms (DECL_TI_ARGS (decl)))
     return true;
 
-  if (type_uses_auto (TREE_TYPE (decl)))
-    {
-      error ("use of %qD before deduction of %<auto%>", decl);
-      return false;
-    }
+  require_deduced_type (decl);
 
   /* If we don't need a value, then we don't need to synthesize DECL.  */
   if (cp_unevaluated_operand != 0)
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 300fe0c..6bac7ec 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1403,7 +1403,10 @@  dump_function_decl (tree t, int flags)
     show_return = !DECL_CONV_FN_P (t)  && !DECL_CONSTRUCTOR_P (t)
 		  && !DECL_DESTRUCTOR_P (t);
   if (show_return)
-    dump_type_prefix (TREE_TYPE (fntype), flags);
+    {
+      tree ret = fndecl_declared_return_type (t);
+      dump_type_prefix (ret, flags);
+    }
 
   /* Print the function name.  */
   if (!do_outer_scope)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6560852..1893482 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -11510,7 +11510,7 @@  cp_parser_decltype (cp_parser *parser)
       cp_lexer_consume_token (parser->lexer);
       if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
 	return error_mark_node;
-      expr = make_auto ();
+      expr = make_decltype_auto ();
       AUTO_IS_DECLTYPE (expr) = true;
       goto rewrite;
     }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3856813..77329a4 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -20672,15 +20672,16 @@  make_args_non_dependent (vec<tree, va_gc> *args)
     }
 }
 
-/* Returns a type which represents 'auto'.  We use a TEMPLATE_TYPE_PARM
-   with a level one deeper than the actual template parms.  */
+/* Returns a type which represents 'auto' or 'decltype(auto)'.  We use a
+   TEMPLATE_TYPE_PARM with a level one deeper than the actual template
+   parms.  */
 
-tree
-make_auto (void)
+static tree
+make_auto_1 (tree name)
 {
   tree au = cxx_make_type (TEMPLATE_TYPE_PARM);
   TYPE_NAME (au) = build_decl (BUILTINS_LOCATION,
-			       TYPE_DECL, get_identifier ("auto"), au);
+			       TYPE_DECL, name, au);
   TYPE_STUB_DECL (au) = TYPE_NAME (au);
   TEMPLATE_TYPE_PARM_INDEX (au) = build_template_parm_index
     (0, processing_template_decl + 1, processing_template_decl + 1,
@@ -20692,6 +20693,18 @@  make_auto (void)
   return au;
 }
 
+tree
+make_decltype_auto (void)
+{
+  return make_auto_1 (get_identifier ("decltype(auto)"));
+}
+
+tree
+make_auto (void)
+{
+  return make_auto_1 (get_identifier ("auto"));
+}
+
 /* Given type ARG, return std::initializer_list<ARG>.  */
 
 static tree
@@ -20756,6 +20769,11 @@  do_auto_deduction (tree type, tree init, tree auto_node)
       bool id = (DECL_P (init) || TREE_CODE (init) == COMPONENT_REF);
       TREE_VEC_ELT (targs, 0)
 	= finish_decltype_type (init, id, tf_warning_or_error);
+      if (type != auto_node)
+	{
+	  error ("%qT as type rather than plain %<decltype(auto)%>", type);
+	  return error_mark_node;
+	}
     }
   else
     {
@@ -20834,13 +20852,15 @@  splice_late_return_type (tree type, tree late_return_type)
   return tsubst (type, argvec, tf_warning_or_error, NULL_TREE);
 }
 
-/* Returns true iff TYPE is a TEMPLATE_TYPE_PARM representing 'auto'.  */
+/* Returns true iff TYPE is a TEMPLATE_TYPE_PARM representing 'auto' or
+   'decltype(auto)'.  */
 
 bool
 is_auto (const_tree type)
 {
   if (TREE_CODE (type) == TEMPLATE_TYPE_PARM
-      && TYPE_IDENTIFIER (type) == get_identifier ("auto"))
+      && (TYPE_IDENTIFIER (type) == get_identifier ("auto")
+	  || TYPE_IDENTIFIER (type) == get_identifier ("decltype(auto)")))
     return true;
   else
     return false;
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index b64398f..b113477 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -1842,8 +1842,8 @@  check_final_overrider (tree overrider, tree basefn)
 {
   tree over_type = TREE_TYPE (overrider);
   tree base_type = TREE_TYPE (basefn);
-  tree over_return = TREE_TYPE (over_type);
-  tree base_return = TREE_TYPE (base_type);
+  tree over_return = fndecl_declared_return_type (overrider);
+  tree base_return = fndecl_declared_return_type (basefn);
   tree over_throw, base_throw;
 
   int fail = 0;
@@ -1897,8 +1897,7 @@  check_final_overrider (tree overrider, tree basefn)
 	{
 	  /* can_convert will permit user defined conversion from a
 	     (reference to) class type. We must reject them.  */
-	  over_return = non_reference (TREE_TYPE (over_type));
-	  if (CLASS_TYPE_P (over_return))
+	  if (CLASS_TYPE_P (non_reference (over_return)))
 	    fail = 2;
 	  else
 	    {
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn18.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn18.C
new file mode 100644
index 0000000..e925033
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn18.C
@@ -0,0 +1,12 @@ 
+// { dg-options "-std=c++1y" }
+
+struct A
+{
+  virtual int f() { return 1; }	 // { dg-message "overriding" }
+  virtual auto g() { return 1; } // { dg-error "virtual" }
+};
+
+struct B: A
+{
+  auto f() { return 1; }	// { dg-error "return type" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn19.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn19.C
new file mode 100644
index 0000000..a6f1606
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn19.C
@@ -0,0 +1,6 @@ 
+// { dg-options "-std=c++1y" }
+
+template <class T>
+auto f() { return T::i; }
+
+extern template auto f<int>(); // does not force instantiation
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn20.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn20.C
new file mode 100644
index 0000000..3025558
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn20.C
@@ -0,0 +1,12 @@ 
+// { dg-options "-std=c++1y" }
+
+template <class T>
+auto f(T) { return 42; }
+template <class T>
+auto g(T) { return 0.0; }
+
+int main()
+{
+  int (*p)(int) = &f; // OK
+  p = &g; // { dg-error "no match" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn21.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn21.C
new file mode 100644
index 0000000..b508af9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn21.C
@@ -0,0 +1,6 @@ 
+// N3638: decltype(auto) must stand alone
+// { dg-options "-std=c++1y" }
+
+void f();
+decltype(auto) g1() { return &f; }
+decltype(auto)* g2() { return f; } // { dg-error "decltype.auto" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C
index dcec899..072f614 100644
--- a/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C
@@ -1,6 +1,6 @@ 
 // { dg-options "-std=c++1y -pedantic-errors" }
 
-auto f() { return 42; }		// { dg-error "deduced return type" }
+auto f() { return 42; }		// { dg-error "old declaration .auto" }
 auto f();			// OK
 int f();			// { dg-error "new declaration" }