diff mbox

C++ PATCH to add auto return type deduction with -std=c++1y

Message ID 4F6E9846.7060702@redhat.com
State New
Headers show

Commit Message

Jason Merrill March 25, 2012, 4 a.m. UTC
As I mentioned in my patch to add -std=c++1y, I've been working on a 
proposal for the next standard to support return type deduction for 
normal functions, not just lambdas.  This patch implements that proposal.

I tried to send this message before with the proposal attached in HTML, 
but the mailing list rejects HTML attachments, so I've dropped it.  I'm 
happy to send it separately to anyone interested.

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

Comments

Marc Glisse March 25, 2012, 10:17 a.m. UTC | #1
On Sun, 25 Mar 2012, Jason Merrill wrote:

> As I mentioned in my patch to add -std=c++1y, I've been working on a proposal 
> for the next standard to support return type deduction for normal functions, 
> not just lambdas.  This patch implements that proposal.

Nice. I like the way you seem to be going for the basic, uncontroversial 
version (extensions can always be discussed later), instead of trying to 
figure out something universal.

If I understand correctly, you pick the first return statement for type 
deduction, and other returns (if any) are only checked afterwards for 
exact consistency, which simplifies the problem quite a bit while still 
allowing some recursion (although interestingly, constexpr functions 
require the use of a single return with ?: whereas your auto functions 
prefer an if and several returns). Naturally, auto functions have to be 
instantiated a bit more eagerly than regular functions, and there is no 
try to sfinae the auto deduction. That seems to fit many simple functions 
quite nicely with little room for unintended consequences.

> I tried to send this message before with the proposal attached in HTML, but 
> the mailing list rejects HTML attachments, so I've dropped it.  I'm happy to 
> send it separately to anyone interested.

I guess I'll have plenty of chances to look at it once it is submitted 
(it isn't like I'd have much to contribute...).
Basile Starynkevitch March 25, 2012, 12:05 p.m. UTC | #2
On Sun, 25 Mar 2012 12:17:10 +0200 (CEST)
Marc Glisse <marc.glisse@inria.fr> wrote:

> On Sun, 25 Mar 2012, Jason Merrill wrote:
> 
> > As I mentioned in my patch to add -std=c++1y, I've been working on a proposal 
> > for the next standard to support return type deduction for normal functions, 
> > not just lambdas.  This patch implements that proposal.
> 
> Nice. I like the way you seem to be going for the basic, uncontroversial 
> version (extensions can always be discussed later), instead of trying to 
> figure out something universal.
> 
> If I understand correctly, you pick the first return statement for type 
> deduction, and other returns (if any) are only checked afterwards for 
> exact consistency [...]

I am not a C++ or a GCC front-end expert, but I am not sure it is the right approach for
functions starting with
   if (!p) return nullptr;
where p is a formal argument.

Or perhaps I am misunderstanding what Marc is saying.

I would rather suggest using all the return statements to infer the type of the function,
not only the first one.

Regards.
Marc Glisse March 25, 2012, 1:32 p.m. UTC | #3
On Sun, 25 Mar 2012, Basile Starynkevitch wrote:

> On Sun, 25 Mar 2012 12:17:10 +0200 (CEST)
> Marc Glisse <marc.glisse@inria.fr> wrote:
>
>> On Sun, 25 Mar 2012, Jason Merrill wrote:
>>
>>> As I mentioned in my patch to add -std=c++1y, I've been working on a proposal
>>> for the next standard to support return type deduction for normal functions,
>>> not just lambdas.  This patch implements that proposal.
>>
>> Nice. I like the way you seem to be going for the basic, uncontroversial
>> version (extensions can always be discussed later), instead of trying to
>> figure out something universal.
>>
>> If I understand correctly, you pick the first return statement for type
>> deduction, and other returns (if any) are only checked afterwards for
>> exact consistency [...]
>
> I am not a C++ or a GCC front-end expert, but I am not sure it is the right approach for
> functions starting with
>   if (!p) return nullptr;
> where p is a formal argument.
>
> Or perhaps I am misunderstanding what Marc is saying.
>
> I would rather suggest using all the return statements to infer the type of the function,
> not only the first one.

First, note that I only explained my understanding from a quick look at 
the testsuite examples and a couple minutes playing with it, the actual 
proposal may well be very different. However:

- a first goal is simple functions, with a single return statement (which 
may even often be the only statement).
- inferring a common type is very hard to define (std::common_type has a 
number of funny properties).
- since he rejects code that has several returns with different types, any 
kind of inference you later manage to add will be 100% compatible.

Yes, it has limitations (it is because I hit them that I guessed the 
"first return statement" rule), but it never does the wrong thing, at 
worst it rejects code that is already invalid today. I agree that 
inference from all returns would be great, later...
Gabriel Dos Reis March 25, 2012, 4:49 p.m. UTC | #4
On Sun, Mar 25, 2012 at 8:32 AM, Marc Glisse <marc.glisse@inria.fr> wrote:

> - a first goal is simple functions, with a single return statement (which
> may even often be the only statement).

yes, this is something we tend to forget: simple things should stay simple, no
matter how clever we think we can get.  for that reason, I like the approach.

-- Gaby
Marc Glisse March 28, 2012, 3:51 p.m. UTC | #5
On Sun, 25 Mar 2012, Marc Glisse wrote:

> - a first goal is simple functions, with a single return statement (which may 
> even often be the only statement).

After playing with it a bit, I am not sure how to use it in the simple 
forwarding case:

T f(int);
auto g(int i){return f(i);}

If T is a reference, this does a copy.

auto&& g(int i){return f(i);}

Now if T is not a reference, this returns a reference to a destroyed 
temporary.

So I am back to writing the following, which is precisely what we never 
want to write:

auto g(int i)->decltype(f(i)){return f(i);}

Maybe having just auto (no auto const&, auto&& and others) without 
stripping cv-ref from it would work better in this case? It would have 
drawbacks in other cases...

I guess the discussion should happen on a different forum once the 
proposal is published...
Gabriel Dos Reis March 28, 2012, 6:29 p.m. UTC | #6
On Wed, Mar 28, 2012 at 10:51 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Sun, 25 Mar 2012, Marc Glisse wrote:
>
>> - a first goal is simple functions, with a single return statement (which
>> may even often be the only statement).
>
>
> After playing with it a bit, I am not sure how to use it in the simple
> forwarding case:
>
> T f(int);
> auto g(int i){return f(i);}

function call or return value is equivalent to initialization. So, the
deduction works (as it should) as if you wrote

    auto x = f(i);

>
> If T is a reference, this does a copy.
>
> auto&& g(int i){return f(i);}
>
> Now if T is not a reference, this returns a reference to a destroyed
> temporary.
>
> So I am back to writing the following, which is precisely what we never want
> to write:
>
> auto g(int i)->decltype(f(i)){return f(i);}
>
> Maybe having just auto (no auto const&, auto&& and others) without stripping
> cv-ref from it would work better in this case? It would have drawbacks in
> other cases...
>
> I guess the discussion should happen on a different forum once the proposal
> is published...
>
> --
> Marc Glisse
Marc Glisse March 28, 2012, 7:20 p.m. UTC | #7
On Wed, 28 Mar 2012, Gabriel Dos Reis wrote:

> On Wed, Mar 28, 2012 at 10:51 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
>> On Sun, 25 Mar 2012, Marc Glisse wrote:
>>
>>> - a first goal is simple functions, with a single return statement (which
>>> may even often be the only statement).
>>
>>
>> After playing with it a bit, I am not sure how to use it in the simple
>> forwarding case:
>>
>> T f(int);
>> auto g(int i){return f(i);}
>
> function call or return value is equivalent to initialization. So, the
> deduction works (as it should) as if you wrote
>
>    auto x = f(i);

I agree that it works like initialization, and like lambdas, so that ship 
has sailed. I assume there were good reasons for that, even if they are 
not obvious, I know things can be trickier than they appear. However, I 
can't help thinking that there is something wrong with having to write:

auto g(int i) -> decltype(f(i)) { return f(i); }

(and even worse with noexcept(noexcept(f(i))), but that's another issue)
for a simple macro-like forwarding function.

Now fixing that isn't the goal of Jason's proposal and this is fine.
Jason Merrill March 28, 2012, 9:03 p.m. UTC | #8
On 03/28/2012 03:20 PM, Marc Glisse wrote:
> I agree that it works like initialization, and like lambdas, so that
> ship has sailed. I assume there were good reasons for that, even if they
> are not obvious, I know things can be trickier than they appear.
> However, I can't help thinking that there is something wrong with having
> to write:
>
> auto g(int i) -> decltype(f(i)) { return f(i); }

I agree that this is a serious problem; decltype has the semantics we 
want, and so new functionality that does something different may not 
actually be very useful.  Unfortunately, making auto functions different 
from auto variables and lambdas isn't very appealing, either.

Jason
Marc Glisse March 29, 2012, 6:04 a.m. UTC | #9
On Wed, 28 Mar 2012, Jason Merrill wrote:

> On 03/28/2012 03:20 PM, Marc Glisse wrote:
>> I agree that it works like initialization, and like lambdas, so that
>> ship has sailed. I assume there were good reasons for that, even if they
>> are not obvious, I know things can be trickier than they appear.
>> However, I can't help thinking that there is something wrong with having
>> to write:
>> 
>> auto g(int i) -> decltype(f(i)) { return f(i); }
>
> I agree that this is a serious problem; decltype has the semantics we want, 
> and so new functionality that does something different may not actually be 
> very useful.

It seems useful to have return type deduction while still being able to 
say: "return by value" or "return const" or "return an lvalue reference", 
which your change provides.

> Unfortunately, making auto functions different from auto 
> variables and lambdas isn't very appealing, either.

Especially lambdas, yes (in N1968, lambdas were supposed to use decltype 
for their return type, it was changed later in the process, I'll have to 
try and find that discussion).

Making them different from auto variables seems quite natural, the 
situations are really different.
Marc Glisse March 29, 2012, 6:58 a.m. UTC | #10
On Wed, 28 Mar 2012, Marc Glisse wrote:

> On Sun, 25 Mar 2012, Marc Glisse wrote:
>
>> - a first goal is simple functions, with a single return statement (which 
>> may even often be the only statement).
>
> After playing with it a bit, I am not sure how to use it in the simple 
> forwarding case:
>
> T f(int);
> auto g(int i){return f(i);}
>
> If T is a reference, this does a copy.
>
> auto&& g(int i){return f(i);}
>
> Now if T is not a reference, this returns a reference to a destroyed 
> temporary.

I haven't given this idea much thought, but here goes:

The main issue seems to be that "auto&&" deduction gives a dangerous 
unusable answer on a temporary. Well, let's change that to make the 
deduction be the same as plain "auto" in that case (how different does 
that make it from decltype?). Sure, it requires some more complicated 
wording. But to users, it could actually be more intuitive as auto&& would 
remain a do-what-I-mean perfect forwarder. And plain "auto" would remain 
compatible with lambdas.
Marc Glisse March 29, 2012, 12:06 p.m. UTC | #11
On Thu, 29 Mar 2012, Marc Glisse wrote:

> On Wed, 28 Mar 2012, Marc Glisse wrote:
>
>> On Sun, 25 Mar 2012, Marc Glisse wrote:
>> 
>>> - a first goal is simple functions, with a single return statement (which 
>>> may even often be the only statement).
>> 
>> After playing with it a bit, I am not sure how to use it in the simple 
>> forwarding case:
>> 
>> T f(int);
>> auto g(int i){return f(i);}
>> 
>> If T is a reference, this does a copy.
>> 
>> auto&& g(int i){return f(i);}
>> 
>> Now if T is not a reference, this returns a reference to a destroyed 
>> temporary.
>
> I haven't given this idea much thought, but here goes:
>
> The main issue seems to be that "auto&&" deduction gives a dangerous unusable 
> answer on a temporary. Well, let's change that to make the deduction be the 
> same as plain "auto" in that case (how different does that make it from 
> decltype?). Sure, it requires some more complicated wording. But to users, it 
> could actually be more intuitive as auto&& would remain a do-what-I-mean 
> perfect forwarder. And plain "auto" would remain compatible with lambdas.

Hmm, the fact that this doesn't fit the usual "deduce auto and collapse 
references with &&" model might make it unacceptable, although it would be 
more useful than rejecting (or strongly warning against) the code for 
temporaries.

Oh well, the current proposal is useful as is, and there is probably room 
left if we later want to add a syntax that implies decltype deduction 
(decltype(auto)? decltype without parenthesis?).
diff mbox

Patch

commit c12876e8b8e5d4ee70b5ea24126eae501af24d3d
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Mar 20 20:13:28 2012 -0400

    	Implement return type deduction for normal functions with -std=c++1y.
    	* cp-tree.h (FNDECL_USED_AUTO): New macro.
    	(LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P): Remove.
    	(dependent_lambda_return_type_node): Remove.
    	(CPTI_DEPENDENT_LAMBDA_RETURN_TYPE): Remove.
    	(struct language_function): Add x_auto_return_pattern field.
    	(current_function_auto_return_pattern): New.
    	(enum tsubst_flags): Add tf_partial.
    	* decl.c (decls_match): Handle auto return comparison.
    	(duplicate_decls): Adjust error message for auto return.
    	(cxx_init_decl_processing): Remove dependent_lambda_return_type_node.
    	(cp_finish_decl): Don't do auto deduction for functions.
    	(grokdeclarator): Allow auto return without trailing return type in
    	C++1y mode.
    	(check_function_type): Defer checking of deduced return type.
    	(start_preparsed_function): Set current_function_auto_return_pattern.
    	(finish_function): Set deduced return type to void if not previously
    	deduced.
    	* decl2.c (change_return_type): Handle error_mark_node.
    	(mark_used): Always instantiate functions with deduced return type.
    	Complain about use if deduction isn't done.
    	* parser.c (cp_parser_lambda_declarator_opt): Use 'auto' for
    	initial return type.
    	(cp_parser_lambda_body): Don't deduce return type in a template.
    	(cp_parser_conversion_type_id): Allow auto in C++1y.
    	* pt.c (instantiate_class_template_1): Don't mess with
    	LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P.
    	(tsubst_copy_and_build): Likewise.
    	(fn_type_unification, tsubst): Don't reduce the template parm level
    	of 'auto' during deduction.
    	(unify): Compare 'auto' specially.
    	(get_bindings): Change test.
    	(always_instantiate_p): Always instantiate functions with deduced
    	return type.
    	(do_auto_deduction): Handle error_mark_node and lambda context.
    	Don't check for use in initializer.
    	(contains_auto_r): Remove.
    	* search.c (lookup_conversions_r): Handle auto conversion function.
    	* semantics.c (lambda_return_type): Handle null return.  Don't mess
    	with dependent_lambda_return_type_node.
    	(apply_deduced_return_type): Rename from apply_lambda_return_type.
    	* typeck.c (merge_types): Handle auto.
    	(check_return_expr): Do auto deduction.
    	* typeck2.c (add_exception_specifier): Fix complain check.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fc60d86..7d986a8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -96,8 +96,8 @@  c-common.h, not after.
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
       STATEMENT_LIST_TRY_BLOCK (in STATEMENT_LIST)
       TYPENAME_IS_RESOLVING_P (in TYPE_NAME_TYPE)
-      LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (in LAMBDA_EXPR)
       TARGET_EXPR_DIRECT_INIT_P (in TARGET_EXPR)
+      FNDECL_USED_AUTO (in FUNCTION_DECL)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -660,11 +660,6 @@  enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_MUTABLE_P(NODE) \
   TREE_LANG_FLAG_1 (LAMBDA_EXPR_CHECK (NODE))
 
-/* True iff we should try to deduce the lambda return type from any return
-   statement.  */
-#define LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P(NODE) \
-  TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
-
 /* The return type in the expression.
  * NULL_TREE indicates that none was specified.  */
 #define LAMBDA_EXPR_RETURN_TYPE(NODE) \
@@ -804,7 +799,6 @@  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,
@@ -876,7 +870,6 @@  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]
@@ -1076,6 +1069,7 @@  struct GTY(()) language_function {
   tree x_in_charge_parm;
   tree x_vtt_parm;
   tree x_return_value;
+  tree x_auto_return_pattern;
 
   BOOL_BITFIELD returns_value : 1;
   BOOL_BITFIELD returns_null : 1;
@@ -1158,6 +1152,11 @@  struct GTY(()) language_function {
 #define current_function_return_value \
   (cp_function_chain->x_return_value)
 
+/* A type involving 'auto' to be used for return type deduction.  */
+
+#define current_function_auto_return_pattern \
+  (cp_function_chain->x_auto_return_pattern)
+
 /* True if NAME is the IDENTIFIER_NODE for an overloaded "operator
    new" or "operator delete".  */
 #define NEW_DELETE_OPNAME_P(NAME)		\
@@ -3085,6 +3084,13 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_LOCAL_FUNCTION_P(NODE) \
   DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE))
 
+/* True if NODE was declared with auto in its return type, but it has
+   started compilation and so the return type might have been changed by
+   return type deduction; its declared return type should be found in
+   DECL_STRUCT_FUNCTION(NODE)->language->x_auto_return_pattern.  */
+#define FNDECL_USED_AUTO(NODE) \
+  TREE_LANG_FLAG_2 (FUNCTION_DECL_CHECK (NODE))
+
 /* Nonzero if NODE is a DECL which we know about but which has not
    been explicitly declared, such as a built-in function or a friend
    declared inside a class.  In the latter case DECL_HIDDEN_FRIEND_P
@@ -4144,6 +4150,8 @@  enum tsubst_flags {
 				    conversion.  */
   tf_no_access_control = 1 << 7, /* Do not perform access checks, even
 				    when issuing other errors.   */
+  tf_partial = 1 << 8,		 /* Doing initial explicit argument
+				    substitution in fn_type_unification.  */
   /* Convenient substitution flags combinations.  */
   tf_warning_or_error = tf_warning | tf_error
 };
@@ -5619,7 +5627,7 @@  extern tree lambda_capture_field_type		(tree);
 extern tree lambda_return_type			(tree);
 extern tree lambda_proxy_type			(tree);
 extern tree lambda_function			(tree);
-extern void apply_lambda_return_type            (tree, tree);
+extern void apply_deduced_return_type           (tree, tree);
 extern tree add_capture                         (tree, tree, tree, bool, bool);
 extern tree add_default_capture                 (tree, tree, tree);
 extern tree build_capture_proxy			(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index e664d43..f021edf 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -960,6 +960,7 @@  decls_match (tree newdecl, tree olddecl)
       tree f2 = TREE_TYPE (olddecl);
       tree p1 = TYPE_ARG_TYPES (f1);
       tree p2 = TYPE_ARG_TYPES (f2);
+      tree r2;
 
       /* Specializations of different templates are different functions
 	 even if they have the same type.  */
@@ -988,7 +989,14 @@  decls_match (tree newdecl, tree olddecl)
       if (TREE_CODE (f1) != TREE_CODE (f2))
 	return 0;
 
-      if (same_type_p (TREE_TYPE (f1), TREE_TYPE (f2)))
+      /* 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);
+
+      if (same_type_p (TREE_TYPE (f1), r2))
 	{
 	  if (!prototype_p (f2) && DECL_EXTERN_C_P (olddecl)
 	      && (DECL_BUILT_IN (olddecl)
@@ -1486,7 +1494,11 @@  duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 			      TYPE_ARG_TYPES (TREE_TYPE (olddecl))))
 	    {
 	      error ("new declaration %q#D", newdecl);
-	      error ("ambiguates old declaration %q+#D", olddecl);
+	      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);
               return error_mark_node;
 	    }
 	  else
@@ -3644,10 +3656,6 @@  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.) */
@@ -6008,8 +6016,8 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
       && (DECL_INITIAL (decl) || init))
     DECL_INITIALIZED_IN_CLASS_P (decl) = 1;
 
-  auto_node = type_uses_auto (type);
-  if (auto_node)
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      && (auto_node = type_uses_auto (type)))
     {
       tree d_init;
       if (init == NULL_TREE)
@@ -9188,9 +9196,13 @@  grokdeclarator (const cp_declarator *declarator,
 		  {
 		    if (!declarator->u.function.late_return_type)
 		      {
-			error ("%qs function uses %<auto%> type specifier without"
-			       " trailing return type", name);
-			return error_mark_node;
+			if (current_class_type
+			    && LAMBDA_TYPE_P (current_class_type))
+			  /* OK for C++11 lambdas.  */;
+			else if (cxx_dialect < cxx1y)
+			  pedwarn (input_location, 0, "%qs function uses "
+				   "%<auto%> type specifier without trailing "
+				   "return type", name);
 		      }
 		    else if (!is_auto (type))
 		      {
@@ -10029,7 +10041,8 @@  grokdeclarator (const cp_declarator *declarator,
       }
     else if (decl_context == FIELD)
       {
-	if (!staticp && type_uses_auto (type))
+	if (!staticp && TREE_CODE (type) != METHOD_TYPE
+	    && type_uses_auto (type))
 	  {
 	    error ("non-static data member declared %<auto%>");
 	    type = error_mark_node;
@@ -12570,7 +12583,8 @@  check_function_type (tree decl, tree current_function_parms)
   /* In a function definition, arg types must be complete.  */
   require_complete_types_for_parms (current_function_parms);
 
-  if (dependent_type_p (return_type))
+  if (dependent_type_p (return_type)
+      || type_uses_auto (return_type))
     return;
   if (!COMPLETE_OR_VOID_TYPE_P (return_type)
       || (TYPE_FOR_JAVA (return_type) && MAYBE_CLASS_TYPE_P (return_type)))
@@ -12741,6 +12755,7 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
 
   /* Build the return declaration for the function.  */
   restype = TREE_TYPE (fntype);
+
   if (DECL_RESULT (decl1) == NULL_TREE)
     {
       tree resdecl;
@@ -12849,6 +12864,12 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
   current_stmt_tree ()->stmts_are_full_exprs_p = 1;
   current_binding_level = bl;
 
+  if (!processing_template_decl && type_uses_auto (restype))
+    {
+      FNDECL_USED_AUTO (decl1) = true;
+      current_function_auto_return_pattern = restype;
+    }
+
   /* Start the statement-tree, start the tree now.  */
   DECL_SAVED_TREE (decl1) = push_stmt_list ();
 
@@ -13463,6 +13484,23 @@  finish_function (int flags)
      of curly braces for a function.  */
   gcc_assert (stmts_are_full_exprs_p ());
 
+  /* If there are no return statements in a function with auto return type,
+     the return type is void.  But if the declared type is something like
+     auto*, this is an error.  */
+  if (!processing_template_decl && FNDECL_USED_AUTO (fndecl)
+      && TREE_TYPE (fntype) == current_function_auto_return_pattern)
+    {
+      if (!is_auto (current_function_auto_return_pattern)
+	  && !current_function_returns_value && !current_function_returns_null)
+	{
+	  error ("no return statements in function returning %qT",
+		 current_function_auto_return_pattern);
+	  inform (input_location, "only plain %<auto%> return type can be "
+		  "deduced to %<void%>");
+	}
+      apply_deduced_return_type (fndecl, void_type_node);
+    }
+
   /* Save constexpr function body before it gets munged by
      the NRV transformation.   */
   maybe_save_function_definition (fndecl);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 7eccf67..b048ac7 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -151,6 +151,9 @@  change_return_type (tree new_ret, tree fntype)
   tree raises = TYPE_RAISES_EXCEPTIONS (fntype);
   tree attrs = TYPE_ATTRIBUTES (fntype);
 
+  if (new_ret == error_mark_node)
+    return fntype;
+
   if (same_type_p (new_ret, TREE_TYPE (fntype)))
     return fntype;
 
@@ -4281,7 +4284,11 @@  mark_used (tree decl)
   if ((TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL)
       || DECL_LANG_SPECIFIC (decl) == NULL
       || DECL_THUNK_P (decl))
-    return true;
+    {
+      if (!processing_template_decl && type_uses_auto (TREE_TYPE (decl)))
+	error ("use of %qD before deduction of %<auto%>", decl);
+      return true;
+    }
 
   /* We only want to do this processing once.  We don't need to keep trying
      to instantiate inline templates, because unit-at-a-time will make sure
@@ -4303,10 +4310,13 @@  mark_used (tree decl)
   /* Normally, we can wait until instantiation-time to synthesize DECL.
      However, if DECL is a static data member initialized with a constant
      or a constexpr function, we need it right now because a reference to
-     such a data member or a call to such function is not value-dependent.  */
+     such a data member or a call to such function is not value-dependent.
+     For a function that uses auto in the return type, we need to instantiate
+     it to find out its type.  */
   if ((decl_maybe_constant_var_p (decl)
        || (TREE_CODE (decl) == FUNCTION_DECL
-	   && DECL_DECLARED_CONSTEXPR_P (decl)))
+	   && (DECL_DECLARED_CONSTEXPR_P (decl)
+	       || type_uses_auto (TREE_TYPE (TREE_TYPE (decl))))))
       && DECL_LANG_SPECIFIC (decl)
       && DECL_TEMPLATE_INFO (decl)
       && !uses_template_parms (DECL_TI_ARGS (decl)))
@@ -4321,6 +4331,9 @@  mark_used (tree decl)
       --function_depth;
     }
 
+  if (type_uses_auto (TREE_TYPE (decl)))
+    error ("use of %qD before deduction of %<auto%>", decl);
+
   /* If we don't need a value, then we don't need to synthesize DECL.  */
   if (cp_unevaluated_operand != 0)
     return true;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 75b7bdb..eac60f1 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -8416,9 +8416,8 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     if (LAMBDA_EXPR_RETURN_TYPE (lambda_expr))
       return_type_specs.type = LAMBDA_EXPR_RETURN_TYPE (lambda_expr);
     else
-      /* Maybe we will deduce the return type later, but we can use void
-	 as a placeholder return type anyways.  */
-      return_type_specs.type = void_type_node;
+      /* Maybe we will deduce the return type later.  */
+      return_type_specs.type = make_auto ();
 
     p = obstack_alloc (&declarator_obstack, 0);
 
@@ -8539,7 +8538,8 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
 
 	if (cp_parser_parse_definitely (parser))
 	  {
-	    apply_lambda_return_type (lambda_expr, lambda_return_type (expr));
+	    if (!processing_template_decl)
+	      apply_deduced_return_type (fco, lambda_return_type (expr));
 
 	    /* Will get error here if type not deduced yet.  */
 	    finish_return_stmt (expr);
@@ -8550,13 +8550,10 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
 
     if (!done)
       {
-	if (!LAMBDA_EXPR_RETURN_TYPE (lambda_expr))
-	  LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (lambda_expr) = true;
 	while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
 	  cp_parser_label_declaration (parser);
 	cp_parser_statement_seq_opt (parser, NULL_TREE);
 	cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
-	LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (lambda_expr) = false;
       }
 
     finish_compound_stmt (compound_stmt);
@@ -11275,8 +11272,14 @@  cp_parser_conversion_type_id (cp_parser* parser)
   if (! cp_parser_uncommitted_to_tentative_parse_p (parser)
       && type_uses_auto (type_specified))
     {
-      error ("invalid use of %<auto%> in conversion operator");
-      return error_mark_node;
+      if (cxx_dialect < cxx1y)
+	{
+	  error ("invalid use of %<auto%> in conversion operator");
+	  return error_mark_node;
+	}
+      else if (template_parm_scope_p ())
+	warning (0, "use of %<auto%> in member template "
+		 "conversion operator can never be deduced");
     }
 
   return type_specified;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b36e49d..f128947 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -9112,12 +9112,6 @@  instantiate_class_template_1 (tree type)
       tree decl = lambda_function (type);
       if (decl)
 	{
-	  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 (decl, false, false);
 	  maybe_add_lambda_conv_op (type);
 	}
@@ -11331,6 +11325,12 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	     about the template parameter in question.  */
 	  return t;
 
+	/* Early in template argument deduction substitution, we don't
+	   want to reduce the level of 'auto', or it will be confused
+	   with a normal template parm in subsequent deduction.  */
+	if (is_auto (t) && (complain & tf_partial))
+	  return t;
+
 	/* If we get here, we must have been looking at a parm for a
 	   more deeply nested template.  Make a new version of this
 	   template parameter, but with a lower level.  */
@@ -14334,14 +14334,8 @@  tsubst_copy_and_build (tree t,
 	  = (LAMBDA_EXPR_DISCRIMINATOR (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);
+	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);
@@ -14860,7 +14854,7 @@  fn_type_unification (tree fn,
       fntype = deduction_tsubst_fntype (fn, converted_args,
 					(explain_p
 					 ? tf_warning_or_error
-					 : tf_none));
+					 : tf_none) | tf_partial);
       processing_template_decl -= incomplete;
 
       if (fntype == error_mark_node)
@@ -16275,7 +16269,8 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	   to see if it matches ARG.  */
 	{
 	  if (TREE_CODE (arg) == TREE_CODE (parm)
-	      && same_type_p (parm, arg))
+	      && (is_auto (parm) ? is_auto (arg)
+		  : same_type_p (parm, arg)))
 	    return unify_success (explain_p);
 	  else
 	    return unify_type_mismatch (explain_p, parm, arg);
@@ -17408,7 +17403,7 @@  get_bindings (tree fn, tree decl, tree explicit_args, bool check_rettype)
      The call to fn_type_unification will handle substitution into the
      FN.  */
   decl_type = TREE_TYPE (decl);
-  if (explicit_args && uses_template_parms (decl_type))
+  if (explicit_args && decl == DECL_TEMPLATE_RESULT (fn))
     {
       tree tmpl;
       tree converted_args;
@@ -18315,7 +18310,8 @@  always_instantiate_p (tree decl)
      that for "extern template" functions.  Therefore, we check
      DECL_DECLARED_INLINE_P, rather than possibly_inlined_p.  */
   return ((TREE_CODE (decl) == FUNCTION_DECL
-	   && DECL_DECLARED_INLINE_P (decl))
+	   && (DECL_DECLARED_INLINE_P (decl)
+	       || type_uses_auto (TREE_TYPE (TREE_TYPE (decl)))))
 	  /* And we need to instantiate static data members so that
 	     their initializers are available in integral constant
 	     expressions.  */
@@ -20269,20 +20265,6 @@  listify_autos (tree type, tree auto_node)
   return tsubst (type, argvec, tf_warning_or_error, NULL_TREE);
 }
 
-/* walk_tree helper for do_auto_deduction.  */
-
-static tree
-contains_auto_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
-		 void *type)
-{
-  /* Is this a variable with the type we're looking for?  */
-  if (DECL_P (*tp)
-      && TREE_TYPE (*tp) == type)
-    return *tp;
-  else
-    return NULL_TREE;
-}
-
 /* Replace occurrences of 'auto' in TYPE with the appropriate type deduced
    from INIT.  AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE.  */
 
@@ -20291,25 +20273,17 @@  do_auto_deduction (tree type, tree init, tree auto_node)
 {
   tree parms, tparms, targs;
   tree args[1];
-  tree decl;
   int val;
 
+  if (init == error_mark_node)
+    return error_mark_node;
+
   if (processing_template_decl
       && (TREE_TYPE (init) == NULL_TREE
 	  || BRACE_ENCLOSED_INITIALIZER_P (init)))
     /* Not enough information to try this yet.  */
     return type;
 
-  /* The name of the object being declared shall not appear in the
-     initializer expression.  */
-  decl = cp_walk_tree_without_duplicates (&init, contains_auto_r, type);
-  if (decl)
-    {
-      error ("variable %q#D with %<auto%> type used in its own "
-	     "initializer", decl);
-      return error_mark_node;
-    }
-
   /* [dcl.spec.auto]: Obtain P from T by replacing the occurrences of auto
      with either a new invented type template parameter U or, if the
      initializer is a braced-init-list (8.5.4), with
@@ -20337,7 +20311,13 @@  do_auto_deduction (tree type, tree init, tree auto_node)
 	/* If type is error_mark_node a diagnostic must have been
 	   emitted by now.  Also, having a mention to '<type error>'
 	   in the diagnostic is not really useful to the user.  */
-	error ("unable to deduce %qT from %qE", type, init);
+	{
+	  if (cfun && auto_node == current_function_auto_return_pattern
+	      && LAMBDA_FUNCTION_P (current_function_decl))
+	    error ("unable to deduce lambda return type from %qE", init);
+	  else
+	    error ("unable to deduce %qT from %qE", type, init);
+	}
       return error_mark_node;
     }
 
@@ -20348,8 +20328,14 @@  do_auto_deduction (tree type, tree init, tree auto_node)
   if (TREE_TYPE (auto_node)
       && !same_type_p (TREE_TYPE (auto_node), TREE_VEC_ELT (targs, 0)))
     {
-      error ("inconsistent deduction for %qT: %qT and then %qT",
-	     auto_node, TREE_TYPE (auto_node), TREE_VEC_ELT (targs, 0));
+      if (cfun && auto_node == current_function_auto_return_pattern
+	  && LAMBDA_FUNCTION_P (current_function_decl))
+	error ("inconsistent types %qT and %qT deduced for "
+	       "lambda return type", TREE_TYPE (auto_node),
+	       TREE_VEC_ELT (targs, 0));
+      else
+	error ("inconsistent deduction for %qT: %qT and then %qT",
+	       auto_node, TREE_TYPE (auto_node), TREE_VEC_ELT (targs, 0));
       return error_mark_node;
     }
   TREE_TYPE (auto_node) = TREE_VEC_ELT (targs, 0);
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index bd1bc57..14d272e 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -2430,6 +2430,11 @@  lookup_conversions_r (tree binfo,
 	  if (!IDENTIFIER_MARKED (name))
 	    {
 	      tree type = DECL_CONV_FN_TYPE (cur);
+	      if (type_uses_auto (type))
+		{
+		  mark_used (cur);
+		  type = DECL_CONV_FN_TYPE (cur);
+		}
 
 	      if (check_hidden_convs (binfo, virtual_depth, virtualness,
 				      type, parent_convs, other_convs))
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5646fa7..6294e19 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -8691,18 +8691,16 @@  begin_lambda_type (tree lambda)
 tree
 lambda_return_type (tree expr)
 {
-  tree type;
+  if (expr == NULL_TREE)
+    return void_type_node;
   if (type_unknown_p (expr)
       || BRACE_ENCLOSED_INITIALIZER_P (expr))
     {
       cxx_incomplete_type_error (expr, TREE_TYPE (expr));
       return void_type_node;
     }
-  if (type_dependent_expression_p (expr))
-    type = dependent_lambda_return_type_node;
-  else
-    type = cv_unqualified (type_decays_to (unlowered_expr_type (expr)));
-  return type;
+  gcc_checking_assert (!type_dependent_expression_p (expr));
+  return cv_unqualified (type_decays_to (unlowered_expr_type (expr)));
 }
 
 /* Given a LAMBDA_EXPR or closure type LAMBDA, return the op() of the
@@ -8749,29 +8747,32 @@  lambda_capture_field_type (tree expr)
   return type;
 }
 
-/* Recompute the return type for LAMBDA with body of the form:
-     { return EXPR ; }  */
+/* Insert the deduced return type for an auto function.  */
 
 void
-apply_lambda_return_type (tree lambda, tree return_type)
+apply_deduced_return_type (tree fco, tree return_type)
 {
-  tree fco = lambda_function (lambda);
   tree result;
 
-  LAMBDA_EXPR_RETURN_TYPE (lambda) = return_type;
-
   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  */
+  if (LAMBDA_FUNCTION_P (fco))
+    {
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (current_class_type);
+      LAMBDA_EXPR_RETURN_TYPE (lambda) = return_type;
+    }
+
+  if (DECL_CONV_FN_P (fco))
+    DECL_NAME (fco) = mangle_conv_op_name_for_type (return_type);
+
   TREE_TYPE (fco) = change_return_type (return_type, TREE_TYPE (fco));
 
   result = DECL_RESULT (fco);
   if (result == NULL_TREE)
     return;
+  if (TREE_TYPE (result) == return_type)
+    return;
 
   /* We already have a DECL_RESULT from start_preparsed_function.
      Now we need to redo the work it and allocate_struct_function
@@ -8786,12 +8787,13 @@  apply_lambda_return_type (tree lambda, tree return_type)
 
   DECL_RESULT (fco) = result;
 
-  if (!processing_template_decl && aggregate_value_p (result, fco))
+  if (!processing_template_decl)
     {
+      bool aggr = aggregate_value_p (result, fco);
 #ifdef PCC_STATIC_STRUCT_RETURN
-      cfun->returns_pcc_struct = 1;
+      cfun->returns_pcc_struct = aggr;
 #endif
-      cfun->returns_struct = 1;
+      cfun->returns_struct = aggr;
     }
 
 }
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index d2d6c4e..b68de52 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -733,6 +733,11 @@  merge_types (tree t1, tree t2)
   if (t2 == error_mark_node)
     return t1;
 
+  /* Handle merging an auto redeclaration with a previous deduced
+     return type.  */
+  if (is_auto (t1))
+    return t2;
+
   /* Merge the attributes.  */
   attributes = (*targetm.merge_type_attributes) (t1, t2);
 
@@ -7779,9 +7784,11 @@  tree
 check_return_expr (tree retval, bool *no_warning)
 {
   tree result;
-  /* The type actually returned by the function, after any
-     promotions.  */
+  /* The type actually returned by the function.  */
   tree valtype;
+  /* The type the function is declared to return, or void if
+     the declared type is incomplete.  */
+  tree functype;
   int fn_returns_value_p;
   bool named_return_value_okay_p;
 
@@ -7812,30 +7819,6 @@  check_return_expr (tree retval, bool *no_warning)
       return NULL_TREE;
     }
 
-  /* As an extension, deduce lambda return type from a return statement
-     anywhere in the body.  */
-  if (retval && LAMBDA_FUNCTION_P (current_function_decl))
-    {
-      tree lambda = CLASSTYPE_LAMBDA_EXPR (current_class_type);
-      if (LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (lambda))
-	{
-	  tree type = lambda_return_type (retval);
-	  tree oldtype = LAMBDA_EXPR_RETURN_TYPE (lambda);
-
-	  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);
-	}
-    }
-
   if (processing_template_decl)
     {
       current_function_returns_value = 1;
@@ -7844,6 +7827,42 @@  check_return_expr (tree retval, bool *no_warning)
       return retval;
     }
 
+  functype = TREE_TYPE (TREE_TYPE (current_function_decl));
+
+  /* Deduce auto return type from a return statement.  */
+  if (current_function_auto_return_pattern)
+    {
+      tree auto_node;
+      tree type;
+
+      if (!retval && !is_auto (current_function_auto_return_pattern))
+	{
+	  /* Give a helpful error message.  */
+	  error ("return-statement with no value, in function returning %qT",
+		 current_function_auto_return_pattern);
+	  inform (input_location, "only plain %<auto%> return type can be "
+		  "deduced to %<void%>");
+	  type = error_mark_node;
+	}
+      else
+	{
+	  if (!retval)
+	    retval = void_zero_node;
+	  auto_node = type_uses_auto (current_function_auto_return_pattern);
+	  type = do_auto_deduction (current_function_auto_return_pattern,
+				    retval, auto_node);
+	}
+
+      if (type == error_mark_node)
+	/* Leave it.  */;
+      else if (functype == current_function_auto_return_pattern)
+	apply_deduced_return_type (current_function_decl, type);
+      else
+	/* A mismatch should have been diagnosed in do_auto_deduction.  */
+	gcc_assert (same_type_p (type, functype));
+      functype = type;
+    }
+
   /* When no explicit return-value is given in a function with a named
      return value, the named return value is used.  */
   result = DECL_RESULT (current_function_decl);
@@ -7857,12 +7876,11 @@  check_return_expr (tree retval, bool *no_warning)
      that's supposed to return a value.  */
   if (!retval && fn_returns_value_p)
     {
-      permerror (input_location, "return-statement with no value, in function returning %qT",
-	         valtype);
-      /* Clear this, so finish_function won't say that we reach the
-	 end of a non-void function (which we don't, we gave a
-	 return!).  */
-      current_function_returns_null = 0;
+      if (functype != error_mark_node)
+	permerror (input_location, "return-statement with no value, in "
+		   "function returning %qT", valtype);
+      /* Remember that this function did return.  */
+      current_function_returns_value = 1;
       /* And signal caller that TREE_NO_WARNING should be set on the
 	 RETURN_EXPR to avoid control reaches end of non-void function
 	 warnings in tree-cfg.c.  */
@@ -7963,14 +7981,12 @@  check_return_expr (tree retval, bool *no_warning)
      && DECL_CONTEXT (retval) == current_function_decl
      && ! TREE_STATIC (retval)
      && ! DECL_ANON_UNION_VAR_P (retval)
-     && (DECL_ALIGN (retval)
-         >= DECL_ALIGN (DECL_RESULT (current_function_decl)))
+     && (DECL_ALIGN (retval) >= DECL_ALIGN (result))
      /* The cv-unqualified type of the returned value must be the
         same as the cv-unqualified return type of the
         function.  */
      && same_type_p ((TYPE_MAIN_VARIANT (TREE_TYPE (retval))),
-                     (TYPE_MAIN_VARIANT
-                      (TREE_TYPE (TREE_TYPE (current_function_decl)))))
+                     (TYPE_MAIN_VARIANT (functype)))
      /* And the returned value must be non-volatile.  */
      && ! TYPE_VOLATILE (TREE_TYPE (retval)));
      
@@ -7995,8 +8011,6 @@  check_return_expr (tree retval, bool *no_warning)
     ;
   else
     {
-      /* The type the function is declared to return.  */
-      tree functype = TREE_TYPE (TREE_TYPE (current_function_decl));
       int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
 
       /* The functype's return type will have been set to void, if it
@@ -8016,10 +8030,9 @@  check_return_expr (tree retval, bool *no_warning)
 	  && DECL_CONTEXT (retval) == current_function_decl
 	  && !TREE_STATIC (retval)
 	  && same_type_p ((TYPE_MAIN_VARIANT (TREE_TYPE (retval))),
-			  (TYPE_MAIN_VARIANT
-			   (TREE_TYPE (TREE_TYPE (current_function_decl)))))
+			  (TYPE_MAIN_VARIANT (functype)))
 	  /* This is only interesting for class type.  */
-	  && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
+	  && CLASS_TYPE_P (functype))
 	flags = flags | LOOKUP_PREFER_RVALUE;
 
       /* First convert the value to the function's return type, then
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 974f92f..80a1d04 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1818,7 +1818,8 @@  add_exception_specifier (tree list, tree spec, int complain)
   else
     diag_type = DK_ERROR; /* error */
 
-  if (diag_type != DK_UNSPECIFIED && complain)
+  if (diag_type != DK_UNSPECIFIED
+      && (complain & tf_warning_or_error))
     cxx_incomplete_type_diagnostic (NULL_TREE, core, diag_type);
 
   return list;
diff --git a/gcc/testsuite/g++.dg/cpp0x/auto18.C b/gcc/testsuite/g++.dg/cpp0x/auto18.C
index 17f7f99..0a59242 100644
--- a/gcc/testsuite/g++.dg/cpp0x/auto18.C
+++ b/gcc/testsuite/g++.dg/cpp0x/auto18.C
@@ -2,5 +2,5 @@ 
 
 void f()
 {
-  auto val = val;  // { dg-error "auto. type used in its own initializer" }
+  auto val = val;  // { dg-error "auto" }
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/auto3.C b/gcc/testsuite/g++.dg/cpp0x/auto3.C
index 860790d..2b51d31 100644
--- a/gcc/testsuite/g++.dg/cpp0x/auto3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/auto3.C
@@ -1,5 +1,5 @@ 
 // Negative test for auto
-// { dg-options "-std=c++0x" }
+// { dg-do compile { target c++11 } }
 
 #include <initializer_list>
 
@@ -10,7 +10,7 @@  auto x;				// { dg-error "auto" }
 auto i = 42, j = 42.0;		// { dg-error "auto" }
 
 // New CWG issue
-auto a[2] = { 1, 2 };		// { dg-error "initializer_list" }
+auto a[2] = { 1, 2 };		// { dg-error "auto|initializer_list" }
 
 template<class T>
 struct A { };
diff --git a/gcc/testsuite/g++.dg/cpp0x/trailing2.C b/gcc/testsuite/g++.dg/cpp0x/trailing2.C
index 5f5af22..91e5557 100644
--- a/gcc/testsuite/g++.dg/cpp0x/trailing2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/trailing2.C
@@ -1,6 +1,6 @@ 
 // PR c++/37967
 // Negative test for auto
-// { dg-options "-std=c++0x" }
+// { dg-do compile { target c++11 } }
 
 auto f1 () -> int;
 auto f2 ();		// { dg-error "without trailing return type" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn1.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn1.C
new file mode 100644
index 0000000..eb54149
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn1.C
@@ -0,0 +1,5 @@ 
+// { dg-options -std=c++1y }
+
+constexpr auto f() { return (char)42; }
+#define SA(X) static_assert ((X),#X)
+SA (f() == 42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn10.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn10.C
new file mode 100644
index 0000000..e3ed3a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn10.C
@@ -0,0 +1,16 @@ 
+// A template declared with auto should be declared with auto in an
+// explicit instantiation or explicit specialization, too.
+// { dg-options -std=c++1y }
+
+template <class T>
+auto f(T t) { return t; }
+
+template<> auto f<int>(int);
+template auto f<float>(float);
+template<> auto f(int*);
+template auto f(float*);
+
+template<> short f<short>(short); // { dg-error "does not match" }
+template char f<char>(char);	  // { dg-error "does not match" }
+template<> short f(short*);	  // { dg-error "does not match" }
+template char f(char*);		  // { dg-error "does not match" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn11.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn11.C
new file mode 100644
index 0000000..a9984aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn11.C
@@ -0,0 +1,5 @@ 
+// { dg-options -std=c++1y }
+
+auto f() { return; } 		// OK, return type is void
+auto* g() { return; }		// { dg-error "no value" }
+auto* h() { }			// { dg-error "no return statements" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn12.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn12.C
new file mode 100644
index 0000000..e4e58e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn12.C
@@ -0,0 +1,14 @@ 
+// { dg-options -std=c++1y }
+// { dg-final { scan-assembler "_ZN1AIiEcviEv" } }
+
+template <class T>
+struct A {
+  T t;
+  operator auto() { return t+1; }
+};
+
+int main()
+{
+  int i = A<int>{42};
+  return (i != 43);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn13.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn13.C
new file mode 100644
index 0000000..34a61ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn13.C
@@ -0,0 +1,6 @@ 
+// { dg-options -std=c++1y }
+
+struct A {
+  template <class T>
+  operator auto() { return T(); } // { dg-warning "auto.*template" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn2.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn2.C
new file mode 100644
index 0000000..4c2cee7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn2.C
@@ -0,0 +1,3 @@ 
+// { dg-options -std=c++1y }
+
+auto f() { return f(); }	// { dg-error "auto" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn3.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn3.C
new file mode 100644
index 0000000..107c37f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn3.C
@@ -0,0 +1,10 @@ 
+// { dg-options -std=c++1y }
+
+bool b;
+auto f()
+{
+  if (b)
+    return 42;
+  else
+    return f();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn4.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn4.C
new file mode 100644
index 0000000..0b76bfc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn4.C
@@ -0,0 +1,7 @@ 
+// { dg-options -std=c++1y }
+
+template <class T>
+constexpr auto f(T t) { return t+1; }
+
+#define SA(X) static_assert((X),#X)
+SA(f(1)==2);
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn5.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn5.C
new file mode 100644
index 0000000..f9af6c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn5.C
@@ -0,0 +1,11 @@ 
+// { dg-options -std=c++1y }
+// { dg-do run }
+
+int i;
+auto& f() { return i; }
+
+int main()
+{
+  f() = 42;
+  return i != 42;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn6.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn6.C
new file mode 100644
index 0000000..03ff537
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn6.C
@@ -0,0 +1,18 @@ 
+// { dg-options -std=c++1y }
+
+template <class T, class U> struct ST;
+template <class T> struct ST<T,T> {};
+
+int g(int);
+char& g(char);
+double&& g(double);
+
+template <class T> auto&& f(T t)
+{ return g(t); }		// { dg-warning "reference to temporary" }
+
+int main()
+{
+  ST<decltype(f(1)),int&&>();
+  ST<decltype(f('\0')),char&>();
+  ST<decltype(f(1.0)),double&&>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn7.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn7.C
new file mode 100644
index 0000000..b915352
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn7.C
@@ -0,0 +1,5 @@ 
+// { dg-options "-std=c++1y -pedantic-errors" }
+
+auto f();
+
+template <class T> auto f(T);
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C
new file mode 100644
index 0000000..dcec899
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn8.C
@@ -0,0 +1,13 @@ 
+// { dg-options "-std=c++1y -pedantic-errors" }
+
+auto f() { return 42; }		// { dg-error "deduced return type" }
+auto f();			// OK
+int f();			// { dg-error "new declaration" }
+
+template <class T> auto f(T t) { return t; }
+template <class T> T f(T t);
+
+int main()
+{
+  f(42);			// { dg-error "ambiguous" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn9.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn9.C
new file mode 100644
index 0000000..1fa7479
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn9.C
@@ -0,0 +1,11 @@ 
+// { dg-options -std=c++1y }
+// { dg-final { scan-assembler "_Z1fIiERDaRKT_S1_" } }
+
+template <class T>
+auto& f(const T& t, T u) { return t; }
+
+int main()
+{
+  int i;
+  f(i,i);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/pr38639.C b/gcc/testsuite/g++.dg/gomp/pr38639.C
index e7145ff..481583e 100644
--- a/gcc/testsuite/g++.dg/gomp/pr38639.C
+++ b/gcc/testsuite/g++.dg/gomp/pr38639.C
@@ -6,7 +6,7 @@  template<int> void
 foo ()
 {
 #pragma omp parallel for
-  for (auto i = i = 0; i<4; ++i)	// { dg-error "incomplete|unable|invalid" }
+  for (auto i = i = 0; i<4; ++i)	// { dg-error "incomplete|unable|invalid|auto" }
     ;
 }
 
diff --git a/gcc/testsuite/g++.dg/warn/pr23075.C b/gcc/testsuite/g++.dg/warn/pr23075.C
index e5b1b48..59e93be 100644
--- a/gcc/testsuite/g++.dg/warn/pr23075.C
+++ b/gcc/testsuite/g++.dg/warn/pr23075.C
@@ -6,4 +6,4 @@  int
 foo (void)
 {
   return;	// { dg-error "with no value" }
-}		// { dg-warning "no return statement" }
+}		// { dg-bogus "no return statement" }
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/spec22.C b/gcc/testsuite/g++.old-deja/g++.pt/spec22.C
index 41aab39..94bffdb 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/spec22.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/spec22.C
@@ -10,6 +10,6 @@  struct S
 
 template <class T> 
 template <> // { dg-error "enclosing class templates|invalid explicit specialization" }
-void S<T>::f<int> ()  // { dg-error "does not match|invalid function declaration" }
+void S<T>::f<int> ()
 {
 }