diff mbox

C++ PATCH for C++17 class template placeholders

Message ID CADzB+2==R4q5QbwzHAT8UZGV6544Ega2WAvvUACkRjqtRJeAEA@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Oct. 4, 2016, 8:42 p.m. UTC
C++17 adds the ability to omit the template arguments for a class
template when declaring a variable with an initializer, much like auto
but supporting a wider variety of initialization.  This is intended to
replace functions like make_tuple.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit beb368fa92a6eb8b55809ab442f59e2fca071cb7
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Oct 4 16:03:17 2016 -0400

    Implement P0091R2, Template argument deduction for class templates.
    
    * parser.c (cp_parser_simple_type_specifier): Parse class placeholder.
    Use the location of the beginning of the type-specifier.
    (cp_parser_init_declarator): Parse deduction guide.
    (cp_parser_diagnose_invalid_type_name): Mention class deduction.
    (cp_parser_type_id_1): Don't accept class placeholder as template arg.
    * cp-tree.h (CLASS_PLACEHOLDER_TEMPLATE): New.
    * decl.c (grokdeclarator): Check for uninitialized auto here.
    (start_decl_1): Not here.
    (cp_finish_decl): Or here.  Don't collapse a list when doing
    class deduction.
    (grokfndecl): Check deduction guide scope and body.
    * error.c (dump_decl, dump_function_decl, dump_function_name):
    Handle deduction guides.
    * pt.c (make_template_placeholder, do_class_deduction): New.
    (build_deduction_guide, rewrite_template_parm): New.
    (dguide_name, dguide_name_p, deduction_guide_p): New.
    (do_auto_deduction): Call do_class_deduction.
    (splice_late_return_type, is_auto): Handle class placeholders.
    (template_parms_level_to_args): Split from template_parms_to_args.
    (tsubst_template_parms_level): Split from tsubst_template_parms.
    * typeck2.c (build_functional_cast): Handle class placeholder.

Comments

Marc Glisse Oct. 5, 2016, 6:58 a.m. UTC | #1
On Tue, 4 Oct 2016, Jason Merrill wrote:

> C++17 adds the ability to omit the template arguments for a class
> template when declaring a variable with an initializer, much like auto
> but supporting a wider variety of initialization.  This is intended to
> replace functions like make_tuple.

Nice. Is there a macro to test for this feature? I couldn't find it in the 
latest sg10 list.
Marek Polacek Oct. 5, 2016, 9:29 a.m. UTC | #2
On Wed, Oct 05, 2016 at 08:58:08AM +0200, Marc Glisse wrote:
> On Tue, 4 Oct 2016, Jason Merrill wrote:
> 
> > C++17 adds the ability to omit the template arguments for a class
> > template when declaring a variable with an initializer, much like auto
> > but supporting a wider variety of initialization.  This is intended to
> > replace functions like make_tuple.
> 
> Nice. Is there a macro to test for this feature? I couldn't find it in the
> latest sg10 list.

And in a similar vein, is there a macro for Selection statements with
initializer?  Can't find it anywhere.

	Marek
Jason Merrill Oct. 5, 2016, 2:04 p.m. UTC | #3
On Wed, Oct 5, 2016 at 5:29 AM, Marek Polacek <polacek@redhat.com> wrote:
> On Wed, Oct 05, 2016 at 08:58:08AM +0200, Marc Glisse wrote:
>> On Tue, 4 Oct 2016, Jason Merrill wrote:
>>
>> > C++17 adds the ability to omit the template arguments for a class
>> > template when declaring a variable with an initializer, much like auto
>> > but supporting a wider variety of initialization.  This is intended to
>> > replace functions like make_tuple.
>>
>> Nice. Is there a macro to test for this feature? I couldn't find it in the
>> latest sg10 list.
>
> And in a similar vein, is there a macro for Selection statements with
> initializer?  Can't find it anywhere.

Not currently, for either.  The rationale for not defining macros for
some features is that for convenience features like this, anyone
concerned about portability will just write code the old way.

Jason
Marc Glisse Oct. 5, 2016, 2:47 p.m. UTC | #4
On Wed, 5 Oct 2016, Jason Merrill wrote:

> On Wed, Oct 5, 2016 at 5:29 AM, Marek Polacek <polacek@redhat.com> wrote:
>> On Wed, Oct 05, 2016 at 08:58:08AM +0200, Marc Glisse wrote:
>>> On Tue, 4 Oct 2016, Jason Merrill wrote:
>>>
>>>> C++17 adds the ability to omit the template arguments for a class
>>>> template when declaring a variable with an initializer, much like auto
>>>> but supporting a wider variety of initialization.  This is intended to
>>>> replace functions like make_tuple.
>>>
>>> Nice. Is there a macro to test for this feature? I couldn't find it in the
>>> latest sg10 list.
>>
>> And in a similar vein, is there a macro for Selection statements with
>> initializer?  Can't find it anywhere.
>
> Not currently, for either.  The rationale for not defining macros for
> some features is that for convenience features like this, anyone
> concerned about portability will just write code the old way.

Thanks. Sadly, this ignores the case of a library that wants to provide an 
explicit deduction guide for its own types to progressive users compiling 
with a C++17 compiler, without preventing C++14 users from using the 
library the old way.
Jason Merrill Oct. 5, 2016, 2:49 p.m. UTC | #5
On Wed, Oct 5, 2016 at 10:47 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Wed, 5 Oct 2016, Jason Merrill wrote:
>> On Wed, Oct 5, 2016 at 5:29 AM, Marek Polacek <polacek@redhat.com> wrote:
>>> On Wed, Oct 05, 2016 at 08:58:08AM +0200, Marc Glisse wrote:
>>>> On Tue, 4 Oct 2016, Jason Merrill wrote:
>>>>
>>>>> C++17 adds the ability to omit the template arguments for a class
>>>>> template when declaring a variable with an initializer, much like auto
>>>>> but supporting a wider variety of initialization.  This is intended to
>>>>> replace functions like make_tuple.
>>>>
>>>> Nice. Is there a macro to test for this feature? I couldn't find it in
>>>> the
>>>> latest sg10 list.
>>>
>>> And in a similar vein, is there a macro for Selection statements with
>>> initializer?  Can't find it anywhere.
>>
>> Not currently, for either.  The rationale for not defining macros for
>> some features is that for convenience features like this, anyone
>> concerned about portability will just write code the old way.
>
> Thanks. Sadly, this ignores the case of a library that wants to provide an
> explicit deduction guide for its own types to progressive users compiling
> with a C++17 compiler, without preventing C++14 users from using the library
> the old way.

True, that's a compelling argument for having a macro for deduction
guides.  I'll propose that to SG10.

Jason
diff mbox

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 85702c5..3fbe1d9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1199,6 +1199,7 @@  extern GTY(()) tree cp_global_trees[CPTI_MAX];
 #define vptr_identifier			cp_global_trees[CPTI_VPTR_IDENTIFIER]
 /* The name of the std namespace.  */
 #define std_identifier			cp_global_trees[CPTI_STD_IDENTIFIER]
+/* The name of a C++17 deduction guide.  */
 #define lang_name_c			cp_global_trees[CPTI_LANG_NAME_C]
 #define lang_name_cplusplus		cp_global_trees[CPTI_LANG_NAME_CPLUSPLUS]
 
@@ -5104,6 +5105,10 @@  enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
 #define TEMPLATE_TYPE_PARAMETER_PACK(NODE) \
   (TEMPLATE_PARM_PARAMETER_PACK (TEMPLATE_TYPE_PARM_INDEX (NODE)))
 
+/* For a C++17 class deduction placeholder, the template it represents.  */
+#define CLASS_PLACEHOLDER_TEMPLATE(NODE) \
+  (DECL_INITIAL (TYPE_NAME (TEMPLATE_TYPE_PARM_CHECK (NODE))))
+
 /* Contexts in which auto deduction occurs. These flags are
    used to control diagnostics in do_auto_deduction.  */
 
@@ -6027,6 +6032,7 @@  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 make_template_placeholder		(tree);
 extern tree do_auto_deduction                   (tree, tree, tree);
 extern tree do_auto_deduction                   (tree, tree, tree,
                                                  tsubst_flags_t,
@@ -6158,6 +6164,9 @@  extern void register_local_specialization       (tree, tree);
 extern tree retrieve_local_specialization       (tree);
 extern tree extract_fnparm_pack                 (tree, tree *);
 extern tree template_parm_to_arg                (tree);
+extern tree dguide_name				(tree);
+extern bool dguide_name_p			(tree);
+extern bool deduction_guide_p			(tree);
 
 /* in repo.c */
 extern void init_repo				(void);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c8f7666..6646062 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5140,7 +5140,7 @@  start_decl_1 (tree decl, bool initialized)
   else if (aggregate_definition_p && !complete_p)
     {
       if (type_uses_auto (type))
-	error ("declaration of %q#D has no initializer", decl);
+	gcc_unreachable ();
       else
 	error ("aggregate %q#D has incomplete type and cannot be defined",
 	       decl);
@@ -6695,12 +6695,11 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	      return;
 	    }
 
-	  error ("declaration of %q#D has no initializer", decl);
-	  TREE_TYPE (decl) = error_mark_node;
-	  return;
+	  gcc_unreachable ();
 	}
       d_init = init;
-      if (TREE_CODE (d_init) == TREE_LIST)
+      if (TREE_CODE (d_init) == TREE_LIST
+	  && !CLASS_PLACEHOLDER_TEMPLATE (auto_node))
 	d_init = build_x_compound_expr_from_list (d_init, ELK_INIT,
 						  tf_warning_or_error);
       d_init = resolve_nondeduced_context (d_init, tf_warning_or_error);
@@ -8182,7 +8181,27 @@  grokfndecl (tree ctype,
 	}
     }
 
-  if (IDENTIFIER_OPNAME_P (DECL_NAME (decl))
+  if (deduction_guide_p (decl))
+    {
+      if (!DECL_NAMESPACE_SCOPE_P (decl))
+	{
+	  error_at (location, "deduction guide %qD must be declared at "
+		    "namespace scope", decl);
+	  return NULL_TREE;
+	}
+      tree type = TREE_TYPE (DECL_NAME (decl));
+      if (CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type))
+	{
+	  error_at (location, "deduction guide %qD must be declared in the "
+		    "same scope as %qT", decl, type);
+	  inform (location_of (type), "  declared here");
+	  return NULL_TREE;
+	}
+      if (funcdef_flag)
+	error_at (location,
+		  "deduction guide %qD must not have a function body", decl);
+    }
+  else if (IDENTIFIER_OPNAME_P (DECL_NAME (decl))
       && !grok_op_properties (decl, /*complain=*/true))
     return NULL_TREE;
   else if (UDLIT_OPER_P (DECL_NAME (decl)))
@@ -11063,12 +11082,19 @@  grokdeclarator (const cp_declarator *declarator,
       }
     else if (decl_context == FIELD)
       {
-	if (!staticp && !friendp && TREE_CODE (type) != METHOD_TYPE
-	    && type_uses_auto (type))
-	  {
-	    error ("non-static data member declared %<auto%>");
-	    type = error_mark_node;
-	  }
+	if (!staticp && !friendp && TREE_CODE (type) != METHOD_TYPE)
+	  if (tree auto_node = type_uses_auto (type))
+	    {
+	      location_t loc = declspecs->locations[ds_type_spec];
+	      if (CLASS_PLACEHOLDER_TEMPLATE (auto_node))
+		error_at (loc, "invalid use of template-name %qE without an "
+			  "argument list",
+			  CLASS_PLACEHOLDER_TEMPLATE (auto_node));
+	      else
+		error_at (loc, "non-static data member declared with "
+			  "placeholder %qT", auto_node);
+	      type = error_mark_node;
+	    }
 
 	/* The C99 flexible array extension.  */
 	if (!staticp && TREE_CODE (type) == ARRAY_TYPE
@@ -11543,6 +11569,22 @@  grokdeclarator (const cp_declarator *declarator,
 	  }
       }
 
+    if (VAR_P (decl) && !initialized)
+      if (tree auto_node = type_uses_auto (type))
+	{
+	  location_t loc = declspecs->locations[ds_type_spec];
+	  if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node))
+	    {
+	      error_at (loc, "invalid use of template-name %qE without an "
+			"argument list", tmpl);
+	      inform (loc, "class template argument deduction "
+		      "requires an initializer");
+	    }
+	  else
+	    error_at (loc, "declaration of %q#D has no initializer", decl);
+	  TREE_TYPE (decl) = error_mark_node;
+	}
+
     if (storage_class == sc_extern && initialized && !funcdef_flag)
       {
 	if (toplevel_bindings_p ())
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 745d7ba..20b20b4 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1159,6 +1159,9 @@  dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	  dump_type (pp, TREE_TYPE (t), flags);
 	  break;
 	}
+      else if (dguide_name_p (t))
+	dump_decl (pp, CLASSTYPE_TI_TEMPLATE (TREE_TYPE (t)),
+		   TFF_PLAIN_IDENTIFIER);
       else
 	pp_cxx_tree_identifier (pp, t);
       break;
@@ -1552,8 +1555,8 @@  dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
   /* Print the return type?  */
   if (show_return)
-    show_return = !DECL_CONV_FN_P (t)  && !DECL_CONSTRUCTOR_P (t)
-		  && !DECL_DESTRUCTOR_P (t);
+    show_return = (!DECL_CONV_FN_P (t)  && !DECL_CONSTRUCTOR_P (t)
+		   && !DECL_DESTRUCTOR_P (t) && !deduction_guide_p (t));
   if (show_return)
     {
       tree ret = fndecl_declared_return_type (t);
@@ -1598,6 +1601,11 @@  dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
       if (show_return)
 	dump_type_suffix (pp, TREE_TYPE (fntype), flags);
+      else if (deduction_guide_p (t))
+	{
+	  pp_cxx_ws_string (pp, "->");
+	  dump_type (pp, TREE_TYPE (TREE_TYPE (t)), flags);
+	}
 
       if (flag_concepts)
         if (tree ci = get_constraints (t))
@@ -1767,10 +1775,6 @@  dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
       pp_cxx_ws_string (pp, "operator");
       dump_type (pp, TREE_TYPE (TREE_TYPE (t)), flags);
     }
-  else if (name && IDENTIFIER_OPNAME_P (name))
-    pp_cxx_tree_identifier (pp, name);
-  else if (name && UDLIT_OPER_P (name))
-    pp_cxx_tree_identifier (pp, name);
   else
     dump_decl (pp, name, flags);
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 8581375..683a6dd 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -3155,6 +3155,9 @@  cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
       error_at (location,
 		"invalid use of template-name %qE without an argument list",
 		decl);
+      if (DECL_CLASS_TEMPLATE_P (decl) && cxx_dialect < cxx1z)
+	inform (location, "class template argument deduction is only available "
+		"with -std=c++1z or -std=gnu++1z");
       inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
     }
   else if (TREE_CODE (id) == BIT_NOT_EXPR)
@@ -12224,6 +12227,9 @@  cp_parser_declaration_seq_opt (cp_parser* parser)
      linkage-specification
      namespace-definition
 
+   C++17:
+     deduction-guide
+
    GNU extension:
 
    declaration:
@@ -16150,7 +16156,7 @@  cp_parser_type_specifier (cp_parser* parser,
      double
      void
 
-   C++0x Extension:
+   C++11 Extension:
 
    simple-type-specifier:
      auto
@@ -16159,6 +16165,10 @@  cp_parser_type_specifier (cp_parser* parser,
      char32_t
      __underlying_type ( type-id )
 
+   C++17 extension:
+
+     nested-name-specifier(opt) template-name
+
    GNU Extension:
 
    simple-type-specifier:
@@ -16429,9 +16439,11 @@  cp_parser_simple_type_specifier (cp_parser* parser,
 
       /* Don't gobble tokens or issue error messages if this is an
 	 optional type-specifier.  */
-      if (flags & CP_PARSER_FLAGS_OPTIONAL)
+      if ((flags & CP_PARSER_FLAGS_OPTIONAL) || cxx_dialect >= cxx1z)
 	cp_parser_parse_tentatively (parser);
 
+      token = cp_lexer_peek_token (parser->lexer);
+
       /* Look for the optional `::' operator.  */
       global_p
 	= (cp_parser_global_scope_opt (parser,
@@ -16445,7 +16457,6 @@  cp_parser_simple_type_specifier (cp_parser* parser,
 						/*type_p=*/false,
 						/*is_declaration=*/false)
 	   != NULL_TREE);
-      token = cp_lexer_peek_token (parser->lexer);
       /* If we have seen a nested-name-specifier, and the next token
 	 is `template', then we are using the template-id production.  */
       if (parser->scope
@@ -16476,9 +16487,50 @@  cp_parser_simple_type_specifier (cp_parser* parser,
 	  && identifier_p (DECL_NAME (type)))
 	maybe_note_name_used_in_class (DECL_NAME (type), type);
       /* If it didn't work out, we don't have a TYPE.  */
-      if ((flags & CP_PARSER_FLAGS_OPTIONAL)
+      if (((flags & CP_PARSER_FLAGS_OPTIONAL) || cxx_dialect >= cxx1z)
 	  && !cp_parser_parse_definitely (parser))
 	type = NULL_TREE;
+      if (!type && cxx_dialect >= cxx1z)
+	{
+	  if (flags & CP_PARSER_FLAGS_OPTIONAL)
+	    cp_parser_parse_tentatively (parser);
+
+	  cp_parser_global_scope_opt (parser,
+				      /*current_scope_valid_p=*/false);
+	  cp_parser_nested_name_specifier_opt (parser,
+					       /*typename_keyword_p=*/false,
+					       /*check_dependency_p=*/true,
+					       /*type_p=*/false,
+					       /*is_declaration=*/false);
+	  tree name = cp_parser_identifier (parser);
+	  if (name && TREE_CODE (name) == IDENTIFIER_NODE
+	      && parser->scope != error_mark_node)
+	    {
+	      tree tmpl = cp_parser_lookup_name (parser, name,
+						 none_type,
+						 /*is_template=*/false,
+						 /*is_namespace=*/false,
+						 /*check_dependency=*/true,
+						 /*ambiguous_decls=*/NULL,
+						 token->location);
+	      if (tmpl && tmpl != error_mark_node
+		  && DECL_CLASS_TEMPLATE_P (tmpl))
+		type = make_template_placeholder (tmpl);
+	      else
+		{
+		  type = error_mark_node;
+		  if (!cp_parser_simulate_error (parser))
+		    cp_parser_name_lookup_error (parser, name, tmpl,
+						 NLE_TYPE, token->location);
+		}
+	    }
+	  else
+	    type = error_mark_node;
+
+	  if ((flags & CP_PARSER_FLAGS_OPTIONAL)
+	      && !cp_parser_parse_definitely (parser))
+	    type = NULL_TREE;
+	}
       if (type && decl_specs)
 	cp_parser_set_decl_spec_type (decl_specs, type,
 				      token,
@@ -18577,10 +18629,28 @@  cp_parser_init_declarator (cp_parser* parser,
      declared.  */
   resume_deferring_access_checks ();
 
+  token = cp_lexer_peek_token (parser->lexer);
+
+  cp_parser_declarator_kind cdk = CP_PARSER_DECLARATOR_NAMED;
+  if (token->type == CPP_OPEN_PAREN
+      && decl_specifiers->type
+      && is_auto (decl_specifiers->type)
+      && CLASS_PLACEHOLDER_TEMPLATE (decl_specifiers->type))
+    {
+      // C++17 deduction guide.
+      cdk = CP_PARSER_DECLARATOR_ABSTRACT;
+
+      for (int i = 0; i < ds_last; ++i)
+	if (i != ds_type_spec
+	    && decl_specifiers->locations[i]
+	    && !cp_parser_simulate_error (parser))
+	  error_at (decl_specifiers->locations[i],
+		    "decl-specifier in declaration of deduction guide");
+    }
+
   /* Parse the declarator.  */
-  token = cp_lexer_peek_token (parser->lexer);
   declarator
-    = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+    = cp_parser_declarator (parser, cdk,
 			    &ctor_dtor_or_conv_p,
 			    /*parenthesized_p=*/NULL,
 			    member_p, friend_p);
@@ -18594,6 +18664,17 @@  cp_parser_init_declarator (cp_parser* parser,
   if (declarator == cp_error_declarator)
     return error_mark_node;
 
+  if (cdk == CP_PARSER_DECLARATOR_ABSTRACT)
+    {
+      gcc_assert (declarator->kind == cdk_function
+		  && !declarator->declarator);
+      tree t = CLASS_PLACEHOLDER_TEMPLATE (decl_specifiers->type);
+      declarator->declarator = make_id_declarator (NULL_TREE, dguide_name (t),
+						   sfk_none);
+      declarator->declarator->id_loc
+	= decl_specifiers->locations[ds_type_spec];
+    }
+
   /* Check that the number of template-parameter-lists is OK.  */
   if (!cp_parser_check_declarator_template_parameters (parser, declarator,
 						       token->location))
@@ -20118,6 +20199,16 @@  cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
   cp_parser_type_specifier_seq (parser, /*is_declaration=*/false,
 				is_trailing_return,
 				&type_specifier_seq);
+  if (is_template_arg && type_specifier_seq.type
+      && TREE_CODE (type_specifier_seq.type) == TEMPLATE_TYPE_PARM
+      && CLASS_PLACEHOLDER_TEMPLATE (type_specifier_seq.type))
+    /* A bare template name as a template argument is a template template
+       argument, not a placeholder, so fail parsing it as a type argument.  */
+    {
+      gcc_assert (cp_parser_uncommitted_to_tentative_parse_p (parser));
+      cp_parser_simulate_error (parser);
+      return error_mark_node;
+    }
   if (type_specifier_seq.type == error_mark_node)
     return error_mark_node;
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2fd1aac..f923666 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -4272,6 +4272,23 @@  template_parm_to_arg (tree t)
   return t;
 }
 
+/* Given a single level of template parameters (a TREE_VEC), return it
+   as a set of template arguments.  */
+
+static tree
+template_parms_level_to_args (tree parms)
+{
+  tree a = copy_node (parms);
+  TREE_TYPE (a) = NULL_TREE;
+  for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
+    TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+
+  if (CHECKING_P)
+    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+
+  return a;
+}
+
 /* Given a set of template parameters, return them as a set of template
    arguments.  The template parameters are represented as a TREE_VEC, in
    the form documented in cp-tree.h for template arguments.  */
@@ -4292,15 +4309,7 @@  template_parms_to_args (tree parms)
 
   for (header = parms; header; header = TREE_CHAIN (header))
     {
-      tree a = copy_node (TREE_VALUE (header));
-      int i;
-
-      TREE_TYPE (a) = NULL_TREE;
-      for (i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
-	TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
-
-      if (CHECKING_P)
-	SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+      tree a = template_parms_level_to_args (TREE_VALUE (header));
 
       if (length > 1)
 	TREE_VEC_ELT (args, --l) = a;
@@ -11357,6 +11366,30 @@  tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   return t;
 }
 
+/* Substitute ARGS into one level PARMS of template parameters.  */
+
+static tree
+tsubst_template_parms_level (tree parms, tree args, tsubst_flags_t complain)
+{
+  if (parms == error_mark_node)
+    return error_mark_node;
+
+  tree new_vec = make_tree_vec (TREE_VEC_LENGTH (parms));
+
+  for (int i = 0; i < TREE_VEC_LENGTH (new_vec); ++i)
+    {
+      tree tuple = TREE_VEC_ELT (parms, i);
+
+      if (tuple == error_mark_node)
+	continue;
+
+      TREE_VEC_ELT (new_vec, i) =
+	tsubst_template_parm (tuple, args, complain);
+    }
+
+  return new_vec;
+}
+
 /* Return the result of substituting ARGS into the template parameters
    given by PARMS.  If there are m levels of ARGS and m + n levels of
    PARMS, then the result will contain n levels of PARMS.  For
@@ -11381,26 +11414,8 @@  tsubst_template_parms (tree parms, tree args, tsubst_flags_t complain)
        new_parms = &(TREE_CHAIN (*new_parms)),
 	 parms = TREE_CHAIN (parms))
     {
-      tree new_vec =
-	make_tree_vec (TREE_VEC_LENGTH (TREE_VALUE (parms)));
-      int i;
-
-      for (i = 0; i < TREE_VEC_LENGTH (new_vec); ++i)
-	{
-          tree tuple;
-
-          if (parms == error_mark_node)
-            continue;
-
-          tuple = TREE_VEC_ELT (TREE_VALUE (parms), i);
-
-          if (tuple == error_mark_node)
-            continue;
-
-	  TREE_VEC_ELT (new_vec, i) =
-	    tsubst_template_parm (tuple, args, complain);
-	}
-
+      tree new_vec = tsubst_template_parms_level (TREE_VALUE (parms),
+						  args, complain);
       *new_parms =
 	tree_cons (size_int (TMPL_PARMS_DEPTH (parms)
 			     - TMPL_ARGS_DEPTH (args)),
@@ -23940,6 +23955,16 @@  make_auto (void)
   return make_auto_1 (get_identifier ("auto"), true);
 }
 
+/* Return a C++17 deduction placeholder for class template TMPL.  */
+
+tree
+make_template_placeholder (tree tmpl)
+{
+  tree t = make_auto_1 (DECL_NAME (tmpl), true);
+  CLASS_PLACEHOLDER_TEMPLATE (t) = tmpl;
+  return t;
+}
+
 /* Make a "constrained auto" type-specifier. This is an
    auto type with constraints that must be associated after
    deduction.  The constraint is formed from the given
@@ -24097,6 +24122,316 @@  extract_autos (tree type)
   return tree_vec;
 }
 
+/* The stem for deduction guide names.  */
+const char *const dguide_base = "__dguide_";
+
+/* Return the name for a deduction guide for class template TMPL.  */
+
+tree
+dguide_name (tree tmpl)
+{
+  tree type = (TYPE_P (tmpl) ? tmpl : TREE_TYPE (tmpl));
+  tree tname = TYPE_IDENTIFIER (type);
+  char *buf = (char *) alloca (1 + strlen (dguide_base)
+			       + IDENTIFIER_LENGTH (tname));
+  memcpy (buf, dguide_base, strlen (dguide_base));
+  memcpy (buf + strlen (dguide_base), IDENTIFIER_POINTER (tname),
+	  IDENTIFIER_LENGTH (tname) + 1);
+  tree dname = get_identifier (buf);
+  TREE_TYPE (dname) = type;
+  return dname;
+}
+
+/* True if NAME is the name of a deduction guide.  */
+
+bool
+dguide_name_p (tree name)
+{
+  return (TREE_TYPE (name)
+	  && !strncmp (IDENTIFIER_POINTER (name), dguide_base,
+		       strlen (dguide_base)));
+}
+
+/* True if FN is a deduction guide.  */
+
+bool
+deduction_guide_p (tree fn)
+{
+  if (tree name = DECL_NAME (fn))
+    return dguide_name_p (name);
+  return false;
+}
+
+/* OLDDECL is a _DECL for a template parameter.  Return a similar parameter at
+   LEVEL:INDEX, using tsubst_args and complain for substitution into non-type
+   template parameter types.  Note that the handling of template template
+   parameters relies on current_template_parms being set appropriately for the
+   new template.  */
+
+static tree
+rewrite_template_parm (tree olddecl, unsigned index, unsigned level,
+		       tree tsubst_args, tsubst_flags_t complain)
+{
+  tree oldidx = get_template_parm_index (olddecl);
+
+  tree newtype;
+  if (TREE_CODE (olddecl) == TYPE_DECL
+      || TREE_CODE (olddecl) == TEMPLATE_DECL)
+    {
+      newtype = copy_type (TREE_TYPE (olddecl));
+      TYPE_MAIN_VARIANT (newtype) = newtype;
+    }
+  else
+    newtype = tsubst (TREE_TYPE (olddecl), tsubst_args,
+		      complain, NULL_TREE);
+
+  tree newdecl
+    = build_decl (DECL_SOURCE_LOCATION (olddecl), TREE_CODE (olddecl),
+		  DECL_NAME (olddecl), newtype);
+  SET_DECL_TEMPLATE_PARM_P (newdecl);
+
+  tree newidx;
+  if (TREE_CODE (olddecl) == TYPE_DECL
+      || TREE_CODE (olddecl) == TEMPLATE_DECL)
+    {
+      newidx = TEMPLATE_TYPE_PARM_INDEX (newtype)
+	= build_template_parm_index (index, level, level,
+				     newdecl, newtype);
+      TYPE_STUB_DECL (newtype) = TYPE_NAME (newtype) = newdecl;
+      TYPE_CANONICAL (newtype) = canonical_type_parameter (newtype);
+
+      if (TREE_CODE (olddecl) == TEMPLATE_DECL)
+	{
+	  DECL_TEMPLATE_RESULT (newdecl)
+	    = build_decl (DECL_SOURCE_LOCATION (olddecl), TYPE_DECL,
+			  DECL_NAME (olddecl), newtype);
+	  DECL_ARTIFICIAL (DECL_TEMPLATE_RESULT (newdecl)) = true;
+	  // First create a copy (ttargs) of tsubst_args with an
+	  // additional level for the template template parameter's own
+	  // template parameters (ttparms).
+	  tree ttparms = (INNERMOST_TEMPLATE_PARMS
+			  (DECL_TEMPLATE_PARMS (olddecl)));
+	  const int depth = TMPL_ARGS_DEPTH (tsubst_args);
+	  tree ttargs = make_tree_vec (depth + 1);
+	  for (int i = 0; i < depth; ++i)
+	    TREE_VEC_ELT (ttargs, i) = TREE_VEC_ELT (tsubst_args, i);
+	  TREE_VEC_ELT (ttargs, depth)
+	    = template_parms_level_to_args (ttparms);
+	  // Substitute ttargs into ttparms to fix references to
+	  // other template parameters.
+	  ttparms = tsubst_template_parms_level (ttparms, ttargs,
+						 complain);
+	  // Now substitute again with args based on tparms, to reduce
+	  // the level of the ttparms.
+	  ttargs = current_template_args ();
+	  ttparms = tsubst_template_parms_level (ttparms, ttargs,
+						 complain);
+	  // Finally, tack the adjusted parms onto tparms.
+	  ttparms = tree_cons (size_int (depth), ttparms,
+			       current_template_parms);
+	  DECL_TEMPLATE_PARMS (newdecl) = ttparms;
+	}
+    }
+  else
+    {
+      tree oldconst = TEMPLATE_PARM_DECL (oldidx);
+      tree newconst
+	= build_decl (DECL_SOURCE_LOCATION (oldconst),
+		      TREE_CODE (oldconst),
+		      DECL_NAME (oldconst), newtype);
+      TREE_CONSTANT (newconst) = TREE_CONSTANT (newdecl)
+	= TREE_READONLY (newconst) = TREE_READONLY (newdecl) = true;
+      SET_DECL_TEMPLATE_PARM_P (newconst);
+      newidx = build_template_parm_index (index, level, level,
+					  newconst, newtype);
+      DECL_INITIAL (newdecl) = DECL_INITIAL (newconst) = newidx;
+    }
+
+  TEMPLATE_PARM_PARAMETER_PACK (newidx)
+    = TEMPLATE_PARM_PARAMETER_PACK (oldidx);
+  return newdecl;
+}
+
+/* Returns a C++17 class deduction guide template based on the constructor
+   CTOR.  */
+
+static tree
+build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
+{
+  if (outer_args)
+    ctor = tsubst (ctor, outer_args, complain, ctor);
+  tree type = DECL_CONTEXT (ctor);
+  tree fn_tmpl;
+  if (TREE_CODE (ctor) == TEMPLATE_DECL)
+    {
+      fn_tmpl = ctor;
+      ctor = DECL_TEMPLATE_RESULT (fn_tmpl);
+    }
+  else
+    fn_tmpl = DECL_TI_TEMPLATE (ctor);
+
+  tree tparms = DECL_TEMPLATE_PARMS (fn_tmpl);
+  /* If type is a member class template, DECL_TI_ARGS (ctor) will have fully
+     specialized args for the enclosing class.  Strip those off, as the
+     deduction guide won't have those template parameters.  */
+  tree targs = get_innermost_template_args (DECL_TI_ARGS (ctor),
+					    TMPL_PARMS_DEPTH (tparms));
+  /* Discard the 'this' parameter.  */
+  tree fparms = FUNCTION_ARG_CHAIN (ctor);
+  tree fargs = TREE_CHAIN (DECL_ARGUMENTS (ctor));
+  tree ci = get_constraints (ctor);
+
+  if (PRIMARY_TEMPLATE_P (fn_tmpl))
+    {
+      /* For a member template constructor, we need to flatten the two template
+	 parameter lists into one, and then adjust the function signature
+	 accordingly.  This gets...complicated.  */
+      ++processing_template_decl;
+      tree save_parms = current_template_parms;
+
+      /* For a member template we should have two levels of parms/args, one for
+	 the class and one for the constructor.  We stripped specialized args
+	 for further enclosing classes above.  */
+      const int depth = 2;
+      gcc_assert (TMPL_ARGS_DEPTH (targs) == depth);
+
+      /* Template args for translating references to the two-level template
+	 parameters into references to the one-level template parameters we are
+	 creating.  */
+      tree tsubst_args = copy_node (targs);
+      TMPL_ARGS_LEVEL (tsubst_args, depth)
+	= copy_node (TMPL_ARGS_LEVEL (tsubst_args, depth));
+
+      /* Template parms for the constructor template.  */
+      tree ftparms = TREE_VALUE (tparms);
+      unsigned flen = TREE_VEC_LENGTH (ftparms);
+      /* Template parms for the class template.  */
+      tparms = TREE_CHAIN (tparms);
+      tree ctparms = TREE_VALUE (tparms);
+      unsigned clen = TREE_VEC_LENGTH (ctparms);
+      /* Template parms for the deduction guide start as a copy of the template
+	 parms for the class.  We set current_template_parms for
+	 lookup_template_class_1.  */
+      current_template_parms = tparms = copy_node (tparms);
+      tree new_vec = TREE_VALUE (tparms) = make_tree_vec (flen + clen);
+      for (unsigned i = 0; i < clen; ++i)
+	TREE_VEC_ELT (new_vec, i) = TREE_VEC_ELT (ctparms, i);
+
+      /* Now we need to rewrite the constructor parms to append them to the
+	 class parms.  */
+      for (unsigned i = 0; i < flen; ++i)
+	{
+	  unsigned index = i + clen;
+	  unsigned level = 1;
+	  tree oldelt = TREE_VEC_ELT (ftparms, i);
+	  tree olddecl = TREE_VALUE (oldelt);
+	  tree newdecl = rewrite_template_parm (olddecl, index, level,
+						tsubst_args, complain);
+	  tree newdef = tsubst_template_arg (TREE_PURPOSE (oldelt),
+					     tsubst_args, complain, ctor);
+	  tree list = build_tree_list (newdef, newdecl);
+	  TEMPLATE_PARM_CONSTRAINTS (list)
+	    = tsubst_constraint_info (TEMPLATE_PARM_CONSTRAINTS (oldelt),
+				      tsubst_args, complain, ctor);
+	  TREE_VEC_ELT (new_vec, index) = list;
+	  TMPL_ARG (tsubst_args, depth, i) = template_parm_to_arg (list);
+	}
+
+      /* Now we have a final set of template parms to substitute into the
+	 function signature.  */
+      targs = template_parms_to_args (tparms);
+      fparms = tsubst (fparms, tsubst_args, complain, ctor);
+      fargs = tsubst (fargs, tsubst_args, complain, ctor);
+      if (ci)
+	ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor);
+
+      current_template_parms = save_parms;
+      --processing_template_decl;
+    }
+
+  tree fntype = build_function_type (type, fparms);
+  tree ded_fn = build_lang_decl_loc (DECL_SOURCE_LOCATION (ctor),
+				     FUNCTION_DECL,
+				     dguide_name (type), fntype);
+  DECL_ARGUMENTS (ded_fn) = fargs;
+  tree ded_tmpl = build_template_decl (ded_fn, tparms, /*member*/false);
+  DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn;
+  TREE_TYPE (ded_tmpl) = TREE_TYPE (ded_fn);
+  DECL_TEMPLATE_INFO (ded_fn) = build_template_info (ded_tmpl, targs);
+  if (ci)
+    set_constraints (ded_tmpl, ci);
+
+  return ded_tmpl;
+}
+
+/* Deduce template arguments for the class template TMPL based on the
+   initializer INIT, and return the resulting type.  */
+
+tree
+do_class_deduction (tree tmpl, tree init, tsubst_flags_t complain)
+{
+  gcc_assert (DECL_CLASS_TEMPLATE_P (tmpl));
+  tree type = TREE_TYPE (tmpl);
+
+  vec<tree,va_gc> *args;
+  if (TREE_CODE (init) == TREE_LIST)
+    args = make_tree_vector_from_list (init);
+  else
+    args = make_tree_vector_single (init);
+
+  if (args->length() == 1)
+    {
+      /* First try to deduce directly, since we don't have implicitly-declared
+	 constructors yet.  */
+      tree parms = build_tree_list (NULL_TREE, type);
+      tree tparms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
+      tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms));
+      int err = type_unification_real (tparms, targs, parms, &(*args)[0],
+				       1, /*subr*/false, DEDUCE_CALL,
+				       LOOKUP_NORMAL, NULL, /*explain*/false);
+      if (err == 0)
+	return tsubst (type, targs, complain, tmpl);
+    }
+
+  tree dname = dguide_name (tmpl);
+  tree cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname,
+				      /*type*/false, /*complain*/false,
+				      /*hidden*/false);
+  if (cands == error_mark_node)
+    cands = NULL_TREE;
+
+  tree outer_args = NULL_TREE;
+  if (DECL_CLASS_SCOPE_P (tmpl)
+      && CLASSTYPE_TEMPLATE_INFO (DECL_CONTEXT (tmpl)))
+    {
+      outer_args = CLASSTYPE_TI_ARGS (DECL_CONTEXT (tmpl));
+      type = TREE_TYPE (most_general_template (tmpl));
+    }
+
+  if (CLASSTYPE_METHOD_VEC (type))
+    // FIXME cache artificial deduction guides
+    for (tree fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns))
+      {
+	tree fn = OVL_CURRENT (fns);
+	tree guide = build_deduction_guide (fn, outer_args, complain);
+	cands = ovl_cons (guide, cands);
+      }
+
+  if (cands == NULL_TREE)
+    {
+      error ("cannot deduce template arguments for %qT, as it has "
+	     "no deduction guides or user-declared constructors", type);
+      return error_mark_node;
+    }
+
+  tree t = build_new_function_call (cands, &args, /*koenig*/false,
+				    complain|tf_decltype);
+
+  release_tree_vector (args);
+
+  return TREE_TYPE (t);
+}
+
 /* 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.  */
 
@@ -24175,6 +24510,9 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	  return error_mark_node;
 	}
     }
+  else if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node))
+    /* C++17 class template argument deduction.  */
+    return do_class_deduction (tmpl, init, complain);
   else
     {
       tree parms = build_tree_list (NULL_TREE, type);
@@ -24274,6 +24612,20 @@  splice_late_return_type (tree type, tree late_return_type)
 {
   if (is_auto (type))
     {
+      if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (type))
+	{
+	  if (!late_return_type)
+	    error ("deduction guide must have trailing return type");
+	  else if (CLASS_TYPE_P (late_return_type)
+		   && CLASSTYPE_TEMPLATE_INFO (late_return_type)
+		   && CLASSTYPE_TI_TEMPLATE (late_return_type) == tmpl)
+	    /* OK */;
+	  else
+	    error ("trailing return type %qT of deduction guide is not "
+		   "a specialization of %qT",
+		   late_return_type, TREE_TYPE (tmpl));
+	}
+
       if (late_return_type)
 	return late_return_type;
 
@@ -24288,14 +24640,15 @@  splice_late_return_type (tree type, tree late_return_type)
 }
 
 /* Returns true iff TYPE is a TEMPLATE_TYPE_PARM representing 'auto' or
-   'decltype(auto)'.  */
+   'decltype(auto)' or a deduced class template.  */
 
 bool
 is_auto (const_tree type)
 {
   if (TREE_CODE (type) == TEMPLATE_TYPE_PARM
       && (TYPE_IDENTIFIER (type) == get_identifier ("auto")
-	  || TYPE_IDENTIFIER (type) == get_identifier ("decltype(auto)")))
+	  || TYPE_IDENTIFIER (type) == get_identifier ("decltype(auto)")
+	  || CLASS_PLACEHOLDER_TEMPLATE (type)))
     return true;
   else
     return false;
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 6e22685..a063ea3 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1952,11 +1952,23 @@  build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
       return error_mark_node;
     }
 
-  if (type_uses_auto (type))
+  if (tree anode = type_uses_auto (type))
     {
-      if (complain & tf_error)
-	error ("invalid use of %<auto%>");
-      return error_mark_node;
+      if (!CLASS_PLACEHOLDER_TEMPLATE (anode))
+	{
+	  if (complain & tf_error)
+	    error ("invalid use of %qT", anode);
+	  return error_mark_node;
+	}
+      else if (!parms)
+	{
+	  if (complain & tf_error)
+	    error ("cannot deduce template arguments for %qT from ()", anode);
+	  return error_mark_node;
+	}
+      else
+	type = do_auto_deduction (type, parms, anode, complain,
+				  adc_variable_type);
     }
 
   if (processing_template_decl)
diff --git a/gcc/testsuite/g++.dg/concepts/class-deduction1.C b/gcc/testsuite/g++.dg/concepts/class-deduction1.C
new file mode 100644
index 0000000..ad48cf8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class-deduction1.C
@@ -0,0 +1,17 @@ 
+// { dg-options "-std=c++1z -fconcepts" }
+
+template <class T>
+concept bool Isint = __is_same_as(T,int);
+
+template <class T>
+struct A
+{
+  int i;
+  A(...);
+};
+
+template <Isint I>
+A(I) -> A<I>;
+
+A a(1);
+A a2(1.0);			// { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept5.C b/gcc/testsuite/g++.dg/concepts/var-concept5.C
index ca16332..b91eb94 100644
--- a/gcc/testsuite/g++.dg/concepts/var-concept5.C
+++ b/gcc/testsuite/g++.dg/concepts/var-concept5.C
@@ -10,5 +10,5 @@  concept bool C2 = true;
 template<C1 T> // { dg-error "not a type" }
 constexpr bool f1( )  { return true; }
 
-template<C2<int> T> // { dg-error "expected" }
+template<C2<int> T> // { dg-error "expected|not a type" }
 constexpr bool f2( )  { return true; }
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction1.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction1.C
new file mode 100644
index 0000000..87fced9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction1.C
@@ -0,0 +1,9 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  A(T);
+};
+
+A a (42);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction10.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction10.C
new file mode 100644
index 0000000..8bc4288
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction10.C
@@ -0,0 +1,11 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  int i;
+  A(...);
+};
+
+template <class T>
+A(T) -> A<T> { }		// { dg-error "1:function body" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction11.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction11.C
new file mode 100644
index 0000000..4e90292
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction11.C
@@ -0,0 +1,11 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  int i;
+  A(...);
+};
+
+template <class T>
+static A(T) -> A<T>;		// { dg-error "1:decl-specifier" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction12.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction12.C
new file mode 100644
index 0000000..9eb541d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction12.C
@@ -0,0 +1,17 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  template<class U, template<U u> class P>
+  A(T,U,P<42>);
+};
+
+template <int I> struct B { };
+
+int i;
+A a(&i,2,B<42>());
+
+template <class,class> class same;
+template <class T> class same<T,T> {};
+same<decltype(a), A<int*>> s;
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction13.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction13.C
new file mode 100644
index 0000000..0e2d235
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction13.C
@@ -0,0 +1,18 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  template <class U>
+  struct B
+  {
+    template <class V>
+    B(T,U,V);
+  };
+};
+
+A<int>::B b(1,2.0,'\3');
+
+template <class,class> class same;
+template <class T> class same<T,T> {};
+same<decltype(b), A<int>::B<double>> s;
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction2.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction2.C
new file mode 100644
index 0000000..736b263
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction2.C
@@ -0,0 +1,10 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  template <class U>
+  A(T, U);
+};
+
+A a (42, 1.0);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction3.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction3.C
new file mode 100644
index 0000000..ed86965
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction3.C
@@ -0,0 +1,14 @@ 
+// { dg-options -std=c++1z }
+
+template <int I>
+struct A { };
+
+template <int I>
+struct B
+{
+  template<template<int>class T>
+  B(T<I>);
+};
+
+A<42> a;
+B b (a);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction4.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction4.C
new file mode 100644
index 0000000..16c41f4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction4.C
@@ -0,0 +1,19 @@ 
+// { dg-options -std=c++1z }
+
+template <int I, int J>
+struct A { };
+
+template <int I>
+struct B
+{
+  template<int J>
+  B(A<I,J>);
+};
+
+A<42,24> a;
+B b (a);
+
+int main()
+{
+  (B(a));
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction5.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction5.C
new file mode 100644
index 0000000..b94a300
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction5.C
@@ -0,0 +1,10 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  int i;
+};
+
+A<int> a1;
+A a(a1);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction6.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction6.C
new file mode 100644
index 0000000..569217d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction6.C
@@ -0,0 +1,11 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  int i;
+};
+
+struct B : A<int> {} b;
+
+A a(b);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction7.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction7.C
new file mode 100644
index 0000000..8e982b9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction7.C
@@ -0,0 +1,10 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  int i;
+};
+
+template <class T>
+A(T);			       // { dg-error "must have trailing return type" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction8.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction8.C
new file mode 100644
index 0000000..3658315
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction8.C
@@ -0,0 +1,15 @@ 
+// { dg-options -std=c++1z }
+
+template <class T>
+struct A
+{
+  int i;
+  A(...);			// { dg-message "candidate" }
+};
+
+A a(42);			// { dg-error "" }
+
+template <class T>
+A(T) -> A<T>;
+
+A a2(42);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C
new file mode 100644
index 0000000..5a2b4f6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C
@@ -0,0 +1,18 @@ 
+// { dg-options -std=c++1z }
+
+namespace N {
+  template <class T>
+  struct A
+  {
+    int i;
+    A(...);
+  };
+}
+
+template <class T>
+N::A(T) -> N::A<T>;		// { dg-error "scope" }
+
+namespace N {
+  template <class T>
+  A(T) -> A<T>;
+}
diff --git a/gcc/testsuite/g++.dg/parse/access10.C b/gcc/testsuite/g++.dg/parse/access10.C
index 62adc1b..d53d317 100644
--- a/gcc/testsuite/g++.dg/parse/access10.C
+++ b/gcc/testsuite/g++.dg/parse/access10.C
@@ -8,4 +8,4 @@  template<int> struct A
 };
 
 // Instead of the bogus error we get a different error.
-// { dg-error "template-name" "" { target *-*-* } 7 }
+// { dg-error "template-name|expected" "" { target *-*-* } 7 }
diff --git a/gcc/testsuite/g++.dg/parse/decl-specifier-1.C b/gcc/testsuite/g++.dg/parse/decl-specifier-1.C
index baf0fe7..5c39b60 100644
--- a/gcc/testsuite/g++.dg/parse/decl-specifier-1.C
+++ b/gcc/testsuite/g++.dg/parse/decl-specifier-1.C
@@ -5,13 +5,12 @@ 
 namespace N
 {
     template<typename> 
-    struct X { };		// { dg-message "N::X" }
+    struct X { };
 }
 
 N::X X;                           // { dg-error "" "" }
 
 int main()
 {
-    return sizeof(X);             // { dg-error "" "" }
-    // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } 15 }
+    return sizeof(X);	    // { dg-prune-output "not declared in this scope" }
 }
diff --git a/gcc/testsuite/g++.dg/parse/template2.C b/gcc/testsuite/g++.dg/parse/template2.C
index 6689c8b..93c7def 100644
--- a/gcc/testsuite/g++.dg/parse/template2.C
+++ b/gcc/testsuite/g++.dg/parse/template2.C
@@ -3,5 +3,5 @@  namespace N {
 }
 
 int main() {
-  N::C(); // { dg-error "template" }
+  N::C(); // { dg-error "template|deduction" }
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.robertl/eb129.C b/gcc/testsuite/g++.old-deja/g++.robertl/eb129.C
index 83fb86b..a8dae01 100644
--- a/gcc/testsuite/g++.old-deja/g++.robertl/eb129.C
+++ b/gcc/testsuite/g++.old-deja/g++.robertl/eb129.C
@@ -17,7 +17,7 @@  int main()
               find_if( l.begin(), l.end(),
                        // This is a typo, it should be bind2nd, but an
                        // ICE is not a very helpful diagnostic!
-                       binder2nd( equal_to<int>(), 2 ) ); // { dg-error "" } 
+                       binder2nd( equal_to<int>(), 2 ) ); // { dg-error "" "" { target c++14_down } }
       assert( *(it) == 2 );
 }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.robertl/eb129a.C b/gcc/testsuite/g++.old-deja/g++.robertl/eb129a.C
index bba5ff0..3150422 100644
--- a/gcc/testsuite/g++.old-deja/g++.robertl/eb129a.C
+++ b/gcc/testsuite/g++.old-deja/g++.robertl/eb129a.C
@@ -16,7 +16,7 @@  int main()
               std::find_if( l.begin(), l.end(),
                        // This is a typo, it should be bind2nd, but an
                        // ICE is not a very helpful diagnostic!
-                       std::binder2nd( std::equal_to<int>(), 2 ) ); // { dg-error "" } 
+                       std::binder2nd( std::equal_to<int>(), 2 ) ); // { dg-error "" "" { target c++14_down } }
       assert( *(it) == 2 );
 }