On 11/16/19 5:51 PM, Jason Merrill wrote:
This patch implements C++20 class template argument deduction for alias templates, which works by a moderately arcane transformation of the deduction guides for the underlying class template.  When implementing it, I found that I could simplify the rules in the draft a bit and get the same effect; I'll be emailing the committee to that effect soon.

While working on this I ran into various shortcomings in our handling of constrained alias templates (like B in the testcase), and fixed those in a separate patch.

Tested x86_64-pc-linux-gnu.  I'm going to clean this up a bit more before checking it in, but am sending this functional patch now before the end of stage 1.

Here's what I'm checking in now:


commit f68b1a418b7b8b294bd274a3028dd4fd6c5e367d
Author: Jason Merrill <ja...@redhat.com>
Date:   Sat Nov 16 15:43:07 2019 -0500

    Fix constrained alias template transparency.
    
    A constrained alias template can't be treated as equivalent to its
    underlying template/type for much the same reason that an alias template
    like void_t can't; we're relying on checking during substitution.
    
            * cxx-pretty-print.c (pp_cxx_unqualified_id): Handle alias
            template-id.
            * pt.c (complex_alias_template_p): True if constraints.
            (get_underlying_template, tsubst): Check alias constraints.
            (push_template_decl_real): Set alias constraints here.
            * parser.c (cp_parser_alias_declaration): Not here.
            * constraint.cc (get_constraints): Take const_tree.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d8e12e99ba3..fd3be60d407 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7772,7 +7772,8 @@ extern cp_expr finish_constraint_and_expr	(location_t, cp_expr, cp_expr);
 extern cp_expr finish_constraint_primary_expr	(cp_expr);
 extern tree finish_concept_definition		(cp_expr, tree);
 extern tree combine_constraint_expressions      (tree, tree);
-extern tree get_constraints                     (tree);
+extern tree append_constraint			(tree, tree);
+extern tree get_constraints                     (const_tree);
 extern void set_constraints                     (tree, tree);
 extern void remove_constraints                  (tree);
 extern tree current_template_constraints	(void);
@@ -7834,6 +7835,7 @@ extern bool subsumes_constraints                (tree, tree);
 extern bool strictly_subsumes			(tree, tree, tree);
 extern bool weakly_subsumes			(tree, tree, tree);
 extern int more_constrained                     (tree, tree);
+extern bool at_least_as_constrained             (tree, tree);
 extern bool constraints_equivalent_p            (tree, tree);
 extern bool atomic_constraints_identical_p	(tree, tree);
 extern hashval_t iterative_hash_constraint      (tree, hashval_t);
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index fadbe7c8ac0..0d1c27a6d16 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1124,7 +1124,7 @@ static GTY ((cache)) decl_tree_cache_map *decl_constraints;
    constrained, return NULL_TREE. Note that T must be non-null. */
 
 tree
-get_constraints (tree t)
+get_constraints (const_tree t)
 {
   if (!flag_concepts)
     return NULL_TREE;
@@ -1134,7 +1134,7 @@ get_constraints (tree t)
   gcc_assert (DECL_P (t));
   if (TREE_CODE (t) == TEMPLATE_DECL)
     t = DECL_TEMPLATE_RESULT (t);
-  tree* found = decl_constraints->get (t);
+  tree* found = decl_constraints->get (CONST_CAST_TREE (t));
   if (found)
     return *found;
   else
@@ -2966,6 +2966,17 @@ more_constrained (tree d1, tree d2)
   return winner;
 }
 
+/* Return whether D1 is at least as constrained as D2.  */
+
+bool
+at_least_as_constrained (tree d1, tree d2)
+{
+  tree n1 = get_normalized_constraints_from_decl (d1);
+  tree n2 = get_normalized_constraints_from_decl (d2);
+
+  return subsumes (n1, n2);
+}
+
 /*---------------------------------------------------------------------------
                         Constraint diagnostics
 ---------------------------------------------------------------------------*/
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 8ece11d276e..909b2a4ef1d 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -172,11 +172,11 @@ pp_cxx_unqualified_id (cxx_pretty_printer *pp, tree t)
     case TYPENAME_TYPE:
     case UNBOUND_CLASS_TEMPLATE:
       pp_cxx_unqualified_id (pp, TYPE_NAME (t));
-      if (CLASS_TYPE_P (t) && CLASSTYPE_USE_TEMPLATE (t))
+      if (tree ti = TYPE_TEMPLATE_INFO_MAYBE_ALIAS (t))
 	{
 	  pp_cxx_begin_template_argument_list (pp);
-	  pp_cxx_template_argument_list (pp, INNERMOST_TEMPLATE_ARGS
-                                                 (CLASSTYPE_TI_ARGS (t)));
+	  tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti));
+	  pp_cxx_template_argument_list (pp, args);
 	  pp_cxx_end_template_argument_list (pp);
 	}
       break;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 27318d3aeed..c08b7b32a32 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -19901,14 +19901,6 @@ cp_parser_alias_declaration (cp_parser* parser)
   if (decl == error_mark_node)
     return decl;
 
-  /* Attach constraints to the alias declaration.  */
-  if (flag_concepts && current_template_parms)
-    {
-      tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
-      tree constr = build_constraints (reqs, NULL_TREE);
-      set_constraints (decl, constr);
-    }
-
   cp_finish_decl (decl, NULL_TREE, 0, NULL_TREE, 0);
 
   if (pushed_scope)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 244eb7d4ff3..6e712bdb4e1 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -215,6 +215,7 @@ static tree listify_autos (tree, tree);
 static tree tsubst_template_parm (tree, tree, tsubst_flags_t);
 static tree instantiate_alias_template (tree, tree, tsubst_flags_t);
 static bool complex_alias_template_p (const_tree tmpl);
+static tree get_underlying_template (tree);
 static tree tsubst_attributes (tree, tree, tsubst_flags_t, tree);
 static tree canonicalize_expr_argument (tree, tsubst_flags_t);
 static tree make_argument_pack (tree);
@@ -5989,9 +5990,18 @@ push_template_decl_real (tree decl, bool is_friend)
 	}
 
       if (TREE_CODE (decl) == TYPE_DECL
-	  && TYPE_DECL_ALIAS_P (decl)
-	  && complex_alias_template_p (tmpl))
-	TEMPLATE_DECL_COMPLEX_ALIAS_P (tmpl) = true;
+	  && TYPE_DECL_ALIAS_P (decl))
+	{
+	  if (tree constr
+	      = TEMPLATE_PARMS_CONSTRAINTS (DECL_TEMPLATE_PARMS (tmpl)))
+	    {
+	      /* ??? Why don't we do this here for all templates?  */
+	      constr = build_constraints (constr, NULL_TREE);
+	      set_constraints (decl, constr);
+	    }
+	  if (complex_alias_template_p (tmpl))
+	    TEMPLATE_DECL_COMPLEX_ALIAS_P (tmpl) = true;
+	}
     }
 
   /* The DECL_TI_ARGS of DECL contains full set of arguments referring
@@ -6350,6 +6360,14 @@ uses_all_template_parms_r (tree t, void *data_)
 static bool
 complex_alias_template_p (const_tree tmpl)
 {
+  /* A renaming alias isn't complex.  */
+  if (get_underlying_template (CONST_CAST_TREE (tmpl)) != tmpl)
+    return false;
+
+  /* Any other constrained alias is complex.  */
+  if (get_constraints (tmpl))
+    return true;
+
   struct uses_all_template_parms_data data;
   tree pat = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
   tree parms = DECL_TEMPLATE_PARMS (tmpl);
@@ -6395,7 +6413,7 @@ dependent_alias_template_spec_p (const_tree t, bool transparent_typedefs)
 /* Return the number of innermost template parameters in TMPL.  */
 
 static int
-num_innermost_template_parms (tree tmpl)
+num_innermost_template_parms (const_tree tmpl)
 {
   tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
   return TREE_VEC_LENGTH (parms);
@@ -6430,6 +6448,11 @@ get_underlying_template (tree tmpl)
       if (!comp_template_args (TI_ARGS (tinfo), alias_args))
 	break;
 
+      /* If TMPL adds or changes any constraints, it isn't equivalent.  I think
+	 it's appropriate to treat a less-constrained alias as equivalent.  */
+      if (!at_least_as_constrained (underlying, tmpl))
+	break;
+
       /* Alias is equivalent.  Strip it and repeat.  */
       tmpl = underlying;
     }
@@ -9679,7 +9702,9 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
          Note that the check is deferred until after the hash
          lookup. This prevents redundant checks on previously
          instantiated specializations. */
-      if (flag_concepts && !constraints_satisfied_p (gen_tmpl, arglist))
+      if (flag_concepts
+	  && !DECL_ALIAS_TEMPLATE_P (gen_tmpl)
+	  && !constraints_satisfied_p (gen_tmpl, arglist))
         {
           if (complain & tf_error)
             {
@@ -20499,8 +20524,6 @@ instantiate_alias_template (tree tmpl, tree args, tsubst_flags_t complain)
 {
   if (tmpl == error_mark_node || args == error_mark_node)
     return error_mark_node;
-  if (!push_tinst_level (tmpl, args))
-    return error_mark_node;
 
   args =
     coerce_innermost_template_parms (DECL_TEMPLATE_PARMS (tmpl),
@@ -20508,6 +20531,22 @@ instantiate_alias_template (tree tmpl, tree args, tsubst_flags_t complain)
 				     /*require_all_args=*/true,
 				     /*use_default_args=*/true);
 
+  /* FIXME check for satisfaction in check_instantiated_args.  */
+  if (flag_concepts
+      && !any_dependent_template_arguments_p (args)
+      && !constraints_satisfied_p (tmpl, args))
+    {
+      if (complain & tf_error)
+	{
+	  auto_diagnostic_group d;
+	  error ("template constraint failure for %qD", tmpl);
+	  diagnose_constraints (input_location, tmpl, args);
+	}
+      return error_mark_node;
+    }
+
+  if (!push_tinst_level (tmpl, args))
+    return error_mark_node;
   tree r = instantiate_template (tmpl, args, complain);
   pop_tinst_level ();
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C b/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C
index 6b2ab0d8046..862879169fb 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C
@@ -7,12 +7,8 @@ template<typename T>
   requires Class<T>
 using X = T*;
 
-// BUG: Alias templates are expanded at the point of use, regardless
-// of whether or not they are dependent. This causes T* to be substituted
-// without acutally checking the constraints. See the declaration of y1
-// below.
 template<typename T>
-using Y = X<T>;
+using Y = X<T>;			// { dg-error "constraint" }
 
 template<Class T> using Z = T*;
 
@@ -20,6 +16,5 @@ struct S { };
 
 X<S> x1; // OK
 X<int> x2; // { dg-error "template constraint failure" }
-Y<int> y1; // { dg-error "" "" { xfail *-*-* } }
+Y<int> y1; // { dg-message "" }
 Z<S> z1; // ok
-
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-alias3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-alias3.C
new file mode 100644
index 00000000000..02e960ad40a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-alias3.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++2a } }
+
+template <typename T> struct A { };
+template <typename T> concept int_type = __is_same_as (T, int);
+template <int_type T> using intA = A<T>;
+
+template <template <typename T> class TT> struct B {
+  TT<char> tt;			// { dg-error "" }
+};
+B<intA> b;
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-alias4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-alias4.C
new file mode 100644
index 00000000000..d37ce6a51e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-alias4.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++2a } }
+
+template <typename T> struct A { };
+template <typename T> concept int_type = __is_same_as (T, int);
+template <int_type T> using intA = A<T>;
+
+template <class T> struct B {
+  intA<T> a;			// { dg-error "" }
+};
+B<char> b;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 0f26009b2be..9dbc61c2151 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,13 @@
+2019-11-16  Jason Merrill  <ja...@redhat.com>
+
+	* cxx-pretty-print.c (pp_cxx_unqualified_id): Handle alias
+	template-id.
+	* pt.c (complex_alias_template_p): True if constraints.
+	(get_underlying_template, tsubst): Check alias constraints.
+	(push_template_decl_real): Set alias constraints here.
+	* parser.c (cp_parser_alias_declaration): Not here.
+	* constraint.cc (get_constraints): Take const_tree.
+
 2019-11-12  Jason Merrill  <ja...@redhat.com>
 
 	PR c++/92206 - ICE with typedef to dependent alias.
commit 5e94d89181f3921d9991c3f86f2a935016e5f4e9
Author: Jason Merrill <ja...@redhat.com>
Date:   Fri Nov 15 16:24:58 2019 -0500

            Implement P1814R0, CTAD for alias templates.
    
    This patch implements C++20 class template argument deduction for alias
    templates, which works by a moderately arcane transformation of the
    deduction guides for the underlying class template.  When implementing it,
    it seemed that I could simplify the rules in the draft a bit and get
    essentially the same effect; I'll be emailing the committee to that effect
    soon.
    
    gcc/cp/
            * pt.c (rewrite_tparm_list): Factor out of build_deduction_guide.
            (maybe_aggr_guide): Check for copy-init here.
            (alias_ctad_tweaks, deduction_guides_for): New.
            (ctor_deduction_guides_for): Factor out of do_class_deduction.
            (ctad_template_p): New.
            * parser.c (cp_parser_simple_type_specifier): Use it.
            * constraint.cc (append_constraint): New.
    gcc/c-family/
            * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_deduction_guides.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fd3be60d407..7e810b8ee7b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6320,6 +6320,7 @@ extern tree build_converted_constant_expr	(tree, tree, tsubst_flags_t);
 extern tree build_converted_constant_bool_expr	(tree, tsubst_flags_t);
 extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
+extern vec<tree,va_gc> *resolve_args (vec<tree,va_gc>*, tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
 extern tree build_cxx_call			(tree, int, tree *,
 						 tsubst_flags_t,
@@ -6820,6 +6821,7 @@ extern tree make_constrained_auto		(tree, tree);
 extern tree make_constrained_decltype_auto	(tree, tree);
 extern tree make_template_placeholder		(tree);
 extern bool template_placeholder_p		(tree);
+extern bool ctad_template_p			(tree);
 extern tree do_auto_deduction                   (tree, tree, tree,
                                                  tsubst_flags_t
 						 = tf_warning_or_error,
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 50066e4dd8b..6491545bc3b 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -980,7 +980,8 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_capture_star_this=201603L");
 	  cpp_define (pfile, "__cpp_inline_variables=201606L");
 	  cpp_define (pfile, "__cpp_aggregate_bases=201603L");
-	  cpp_define (pfile, "__cpp_deduction_guides=201703L");
+	  if (cxx_dialect <= cxx17)
+	    cpp_define (pfile, "__cpp_deduction_guides=201703L");
 	  cpp_define (pfile, "__cpp_noexcept_function_type=201510L");
 	  /* Old macro, superseded by
 	     __cpp_nontype_template_parameter_auto.  */
@@ -1000,6 +1001,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
 	  cpp_define (pfile, "__cpp_consteval=201811L");
 	  cpp_define (pfile, "__cpp_constinit=201907L");
+	  cpp_define (pfile, "__cpp_deduction_guides=201907L");
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 8bfe3368816..acc7e1322d0 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4398,7 +4398,7 @@ build_converted_constant_bool_expr (tree expr, tsubst_flags_t complain)
 
 /* Do any initial processing on the arguments to a function call.  */
 
-static vec<tree, va_gc> *
+vec<tree, va_gc> *
 resolve_args (vec<tree, va_gc> *args, tsubst_flags_t complain)
 {
   unsigned int ix;
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 0d1c27a6d16..533277a758f 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1116,6 +1116,25 @@ build_constraints (tree tr, tree dr)
   return (tree)ci;
 }
 
+/* Add constraint RHS to the end of CONSTRAINT_INFO ci.  */
+
+tree
+append_constraint (tree ci, tree rhs)
+{
+  tree tr = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE;
+  tree dr = ci ? CI_DECLARATOR_REQS (ci) : NULL_TREE;
+  dr = combine_constraint_expressions (dr, rhs);
+  if (ci)
+    {
+      CI_DECLARATOR_REQS (ci) = dr;
+      tree ac = combine_constraint_expressions (tr, dr);
+      CI_ASSOCIATED_CONSTRAINTS (ci) = ac;
+    }
+  else
+    ci = build_constraints (tr, dr);
+  return ci;
+}
+
 /* A mapping from declarations to constraint information.  */
 
 static GTY ((cache)) decl_tree_cache_map *decl_constraints;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c08b7b32a32..ed2441644f1 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18082,8 +18082,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 						 /*ambiguous_decls=*/NULL,
 						 token->location);
 	      if (tmpl && tmpl != error_mark_node
-		  && (DECL_CLASS_TEMPLATE_P (tmpl)
-		      || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)))
+		  && ctad_template_p (tmpl))
 		type = make_template_placeholder (tmpl);
 	      else if (flag_concepts && tmpl && concept_definition_p (tmpl))
 		type = cp_parser_placeholder_type_specifier (parser, loc,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 6e712bdb4e1..5088dc125ce 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -27784,6 +27784,29 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level,
   return newdecl;
 }
 
+/* As rewrite_template_parm, but for the whole TREE_LIST representing a
+   template parameter.  */
+
+static tree
+rewrite_tparm_list (tree oldelt, unsigned index, unsigned level,
+		    tree targs, unsigned targs_index, tsubst_flags_t complain)
+{
+  tree olddecl = TREE_VALUE (oldelt);
+  tree newdecl = rewrite_template_parm (olddecl, index, level,
+					targs, complain);
+  if (newdecl == error_mark_node)
+    return error_mark_node;
+  tree newdef = tsubst_template_arg (TREE_PURPOSE (oldelt),
+				     targs, complain, NULL_TREE);
+  tree list = build_tree_list (newdef, newdecl);
+  TEMPLATE_PARM_CONSTRAINTS (list)
+    = tsubst_constraint_info (TEMPLATE_PARM_CONSTRAINTS (oldelt),
+			      targs, complain, NULL_TREE);
+  int depth = TMPL_ARGS_DEPTH (targs);
+  TMPL_ARG (targs, depth, targs_index) = template_parm_to_arg (list);
+  return list;
+}
+
 /* Returns a C++17 class deduction guide template based on the constructor
    CTOR.  As a special case, CTOR can be a RECORD_TYPE for an implicit default
    guide, REFERENCE_TYPE for an implicit copy/move guide, or TREE_LIST for an
@@ -27897,19 +27920,12 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com
 	      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);
-	      if (newdecl == error_mark_node)
+	      tree newelt
+		= rewrite_tparm_list (oldelt, index, level,
+				      tsubst_args, i, complain);
+	      if (newelt == error_mark_node)
 		ok = false;
-	      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);
+	      TREE_VEC_ELT (new_vec, index) = newelt;
 	    }
 
 	  /* Now we have a final set of template parms to substitute into the
@@ -27984,20 +28000,48 @@ collect_ctor_idx_types (tree ctor, tree list)
   return list;
 }
 
+/* Return whether ETYPE is, or is derived from, a specialization of TMPL.  */
+
+static bool
+is_spec_or_derived (tree etype, tree tmpl)
+{
+  if (!etype || !CLASS_TYPE_P (etype))
+    return false;
+
+  tree type = TREE_TYPE (tmpl);
+  tree tparms = (INNERMOST_TEMPLATE_PARMS
+		 (DECL_TEMPLATE_PARMS (tmpl)));
+  tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms));
+  int err = unify (tparms, targs, type, etype,
+		   UNIFY_ALLOW_DERIVED, /*explain*/false);
+  ggc_free (targs);
+  return !err;
+}
+
 /* Return a C++20 aggregate deduction candidate for TYPE initialized from
    INIT.  */
 
 static tree
-maybe_aggr_guide (tree type, tree init)
+maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
 {
   if (cxx_dialect < cxx2a)
     return NULL_TREE;
 
   if (init == NULL_TREE)
     return NULL_TREE;
+
+  tree type = TREE_TYPE (tmpl);
   if (!CP_AGGREGATE_TYPE_P (type))
     return NULL_TREE;
 
+  /* No aggregate candidate for copy-initialization.  */
+  if (args->length() == 1)
+    {
+      tree val = (*args)[0];
+      if (is_spec_or_derived (tmpl, TREE_TYPE (val)))
+	return NULL_TREE;
+    }
+
   /* If we encounter a problem, we just won't add the candidate.  */
   tsubst_flags_t complain = tf_none;
 
@@ -28039,22 +28083,292 @@ maybe_aggr_guide (tree type, tree init)
   return NULL_TREE;
 }
 
-/* Return whether ETYPE is, or is derived from, a specialization of TMPL.  */
+/* UGUIDES are the deduction guides for the underlying template of alias
+   template TMPL; adjust them to be deduction guides for TMPL.  */
 
-static bool
-is_spec_or_derived (tree etype, tree tmpl)
+static tree
+alias_ctad_tweaks (tree tmpl, tree uguides)
 {
-  if (!etype || !CLASS_TYPE_P (etype))
-    return false;
+  /* [over.match.class.deduct]: When resolving a placeholder for a deduced
+     class type (9.2.8.2) where the template-name names an alias template A,
+     the defining-type-id of A must be of the form
 
+     typename(opt) nested-name-specifier(opt) template(opt) simple-template-id
+
+     as specified in 9.2.8.2. The guides of A are the set of functions or
+     function templates formed as follows. For each function or function
+     template f in the guides of the template named by the simple-template-id
+     of the defining-type-id, the template arguments of the return type of f
+     are deduced from the defining-type-id of A according to the process in
+     13.10.2.5 with the exception that deduction does not fail if not all
+     template arguments are deduced. Let g denote the result of substituting
+     these deductions into f. If substitution succeeds, form a function or
+     function template f' with the following properties and add it to the set
+     of guides of A:
+
+     * The function type of f' is the function type of g.
+
+     * If f is a function template, f' is a function template whose template
+     parameter list consists of all the template parameters of A (including
+     their default template arguments) that appear in the above deductions or
+     (recursively) in their default template arguments, followed by the
+     template parameters of f that were not deduced (including their default
+     template arguments), otherwise f' is not a function template.
+
+     * The associated constraints (13.5.2) are the conjunction of the
+     associated constraints of g and a constraint that is satisfied if and only
+     if the arguments of A are deducible (see below) from the return type.
+
+     * If f is a copy deduction candidate (12.4.1.8), then f' is considered to
+     be so as well.
+
+     * If f was generated from a deduction-guide (12.4.1.8), then f' is
+     considered to be so as well.
+
+     * The explicit-specifier of f' is the explicit-specifier of g (if
+     any).  */
+
+  /* This implementation differs from the above in two significant ways:
+
+     1) We include all template parameters of A, not just some.
+     2) The added constraint is same_type instead of deducible.
+
+     I believe that while it's probably possible to construct a testcase that
+     behaves differently with this simplification, it should have the same
+     effect for real uses.  Including all template parameters means that we
+     deduce all parameters of A when resolving the call, so when we're in the
+     constraint we don't need to deduce them again, we can just check whether
+     the deduction produced the desired result.  */
+
+  tsubst_flags_t complain = tf_warning_or_error;
+  tree atype = TREE_TYPE (tmpl);
+  tree aguides = NULL_TREE;
+  tree atparms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
+  unsigned natparms = TREE_VEC_LENGTH (atparms);
+  tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+  for (ovl_iterator iter (uguides); iter; ++iter)
+    {
+      tree f = *iter;
+      tree in_decl = f;
+      location_t loc = DECL_SOURCE_LOCATION (f);
+      tree ret = TREE_TYPE (TREE_TYPE (f));
+      tree fprime = f;
+      if (TREE_CODE (f) == TEMPLATE_DECL)
+	{
+	  processing_template_decl_sentinel ptds (/*reset*/false);
+	  ++processing_template_decl;
+
+	  /* Deduce template arguments for f from the type-id of A.  */
+	  tree ftparms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (f));
+	  unsigned len = TREE_VEC_LENGTH (ftparms);
+	  tree targs = make_tree_vec (len);
+	  int err = unify (ftparms, targs, ret, utype, UNIFY_ALLOW_NONE, false);
+	  gcc_assert (!err);
+
+	  /* The number of parms for f' is the number of parms for A plus
+	     non-deduced parms of f.  */
+	  unsigned ndlen = 0;
+	  unsigned j;
+	  for (unsigned i = 0; i < len; ++i)
+	    if (TREE_VEC_ELT (targs, i) == NULL_TREE)
+	      ++ndlen;
+	  tree gtparms = make_tree_vec (natparms + ndlen);
+
+	  /* First copy over the parms of A.  */
+	  for (j = 0; j < natparms; ++j)
+	    TREE_VEC_ELT (gtparms, j) = TREE_VEC_ELT (atparms, j);
+	  /* Now rewrite the non-deduced parms of f.  */
+	  for (unsigned i = 0; ndlen && i < len; ++i)
+	    if (TREE_VEC_ELT (targs, i) == NULL_TREE)
+	      {
+		--ndlen;
+		unsigned index = j++;
+		unsigned level = 1;
+		tree oldlist = TREE_VEC_ELT (ftparms, i);
+		tree list = rewrite_tparm_list (oldlist, index, level,
+						targs, i, complain);
+		TREE_VEC_ELT (gtparms, index) = list;
+	      }
+	  gtparms = build_tree_list (size_one_node, gtparms);
+
+	  /* Substitute the deduced arguments plus the rewritten template
+	     parameters into f to get g.  This covers the type, copyness,
+	     guideness, and explicit-specifier.  */
+	  tree g = tsubst_decl (DECL_TEMPLATE_RESULT (f), targs, complain);
+	  if (g == error_mark_node)
+	    return error_mark_node;
+	  DECL_USE_TEMPLATE (g) = 0;
+	  fprime = build_template_decl (g, gtparms, false);
+	  DECL_TEMPLATE_RESULT (fprime) = g;
+	  TREE_TYPE (fprime) = TREE_TYPE (g);
+	  tree gtargs = template_parms_to_args (gtparms);
+	  DECL_TEMPLATE_INFO (g) = build_template_info (fprime, gtargs);
+	  DECL_PRIMARY_TEMPLATE (fprime) = fprime;
+
+	  /* Substitute the associated constraints.  */
+	  tree ci = get_constraints (f);
+	  if (ci)
+	    ci = tsubst_constraint_info (ci, targs, complain, in_decl);
+	  if (ci == error_mark_node)
+	    return error_mark_node;
+
+	  /* Add a constraint that the return type matches the instantiation of
+	     A with the same template arguments.  */
+	  ret = TREE_TYPE (TREE_TYPE (fprime));
+	  if (!same_type_p (atype, ret)
+	      /* FIXME this should mean they don't compare as equivalent.  */
+	      || dependent_alias_template_spec_p (atype, nt_opaque))
+	    {
+	      tree same = finish_trait_expr (loc, CPTK_IS_SAME_AS, atype, ret);
+	      ci = append_constraint (ci, same);
+	    }
+
+	  if (ci)
+	    set_constraints (fprime, ci);
+	}
+      else
+	{
+	  /* For a non-template deduction guide, if the arguments of A aren't
+	     deducible from the return type, don't add the candidate.  */
+	  tree targs = make_tree_vec (natparms);
+	  int err = unify (atparms, targs, utype, ret, UNIFY_ALLOW_NONE, false);
+	  for (unsigned i = 0; !err && i < natparms; ++i)
+	    if (TREE_VEC_ELT (targs, i) == NULL_TREE)
+	      err = true;
+	  if (err)
+	    continue;
+	}
+
+      aguides = lookup_add (fprime, aguides);
+    }
+
+  return aguides;
+}
+
+/* Return artificial deduction guides built from the constructors of class
+   template TMPL.  */
+
+static tree
+ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
+{
   tree type = TREE_TYPE (tmpl);
-  tree tparms = (INNERMOST_TEMPLATE_PARMS
-		 (DECL_TEMPLATE_PARMS (tmpl)));
-  tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms));
-  int err = unify (tparms, targs, type, etype,
-		   UNIFY_ALLOW_DERIVED, /*explain*/false);
-  ggc_free (targs);
-  return !err;
+  tree outer_args = NULL_TREE;
+  if (DECL_CLASS_SCOPE_P (tmpl)
+      && CLASSTYPE_TEMPLATE_INSTANTIATION (DECL_CONTEXT (tmpl)))
+    {
+      outer_args = CLASSTYPE_TI_ARGS (DECL_CONTEXT (tmpl));
+      type = TREE_TYPE (most_general_template (tmpl));
+    }
+
+  tree cands = NULL_TREE;
+
+  for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter)
+    {
+      /* Skip inherited constructors.  */
+      if (iter.using_p ())
+	continue;
+
+      tree guide = build_deduction_guide (type, *iter, outer_args, complain);
+      cands = lookup_add (guide, cands);
+    }
+
+  /* Add implicit default constructor deduction guide.  */
+  if (!TYPE_HAS_USER_CONSTRUCTOR (type))
+    {
+      tree guide = build_deduction_guide (type, type, outer_args,
+					  complain);
+      cands = lookup_add (guide, cands);
+    }
+
+  /* Add copy guide.  */
+  {
+    tree gtype = build_reference_type (type);
+    tree guide = build_deduction_guide (type, gtype, outer_args,
+					complain);
+    cands = lookup_add (guide, cands);
+  }
+
+  return cands;
+}
+
+static GTY((deletable)) hash_map<tree, tree_pair_p> *dguide_cache;
+
+/* Return the non-aggregate deduction guides for deducible template TMPL.  The
+   aggregate candidate is added separately because it depends on the
+   initializer.  */
+
+static tree
+deduction_guides_for (tree tmpl, tsubst_flags_t complain)
+{
+  tree guides = NULL_TREE;
+  if (DECL_ALIAS_TEMPLATE_P (tmpl))
+    {
+      tree under = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+      tree tinfo = get_template_info (under);
+      guides = deduction_guides_for (TI_TEMPLATE (tinfo), complain);
+    }
+  else
+    {
+      guides = lookup_qualified_name (CP_DECL_CONTEXT (tmpl),
+				      dguide_name (tmpl),
+				      /*type*/false, /*complain*/false,
+				      /*hidden*/false);
+      if (guides == error_mark_node)
+	guides = NULL_TREE;
+    }
+
+  /* Cache the deduction guides for a template.  We also remember the result of
+     lookup, and rebuild everything if it changes; should be very rare.  */
+  tree_pair_p cache = NULL;
+  if (tree_pair_p &r
+      = hash_map_safe_get_or_insert<hm_ggc> (dguide_cache, tmpl))
+    {
+      cache = r;
+      if (cache->purpose == guides)
+	return cache->value;
+    }
+  else
+    {
+      r = cache = ggc_cleared_alloc<tree_pair_s> ();
+      cache->purpose = guides;
+    }
+
+  tree cands = NULL_TREE;
+  if (DECL_ALIAS_TEMPLATE_P (tmpl))
+    cands = alias_ctad_tweaks (tmpl, guides);
+  else
+    {
+      cands = ctor_deduction_guides_for (tmpl, complain);
+      for (ovl_iterator it (guides); it; ++it)
+	cands = lookup_add (*it, cands);
+    }
+
+  cache->value = cands;
+  return cands;
+}
+
+/* Return whether TMPL is a (class template argument-) deducible template.  */
+
+bool
+ctad_template_p (tree tmpl)
+{
+  /* A deducible template is either a class template or is an alias template
+     whose defining-type-id is of the form
+
+      typename(opt) nested-name-specifier(opt) template(opt) simple-template-id
+
+     where the nested-name-specifier (if any) is non-dependent and the
+     template-name of the simple-template-id names a deducible template.  */
+
+  if (DECL_CLASS_TEMPLATE_P (tmpl)
+      || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
+    return true;
+  if (!DECL_ALIAS_TEMPLATE_P (tmpl))
+    return false;
+  tree orig = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+  if (tree tinfo = get_template_info (orig))
+    return ctad_template_p (TI_TEMPLATE (tinfo));
+  return false;
 }
 
 /* Deduce template arguments for the class template placeholder PTYPE for
@@ -28062,18 +28376,29 @@ is_spec_or_derived (tree etype, tree tmpl)
    type.  */
 
 static tree
-do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
-		    tsubst_flags_t complain)
+do_class_deduction (tree ptype, tree tmpl, tree init,
+		    int flags, tsubst_flags_t complain)
 {
-  if (!DECL_CLASS_TEMPLATE_P (tmpl))
+  /* We should have handled this in the caller.  */
+  if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
+    return ptype;
+
+  /* Look through alias templates that just rename another template.  */
+  tmpl = get_underlying_template (tmpl);
+  if (!ctad_template_p (tmpl))
     {
-      /* We should have handled this in the caller.  */
-      if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
-	return ptype;
       if (complain & tf_error)
-	error ("non-class template %qT used without template arguments", tmpl);
+	error ("non-deducible template %qT used without template arguments", tmpl);
       return error_mark_node;
     }
+  else if (cxx_dialect < cxx2a && DECL_ALIAS_TEMPLATE_P (tmpl))
+    {
+      /* This doesn't affect conforming C++17 code, so just pedwarn.  */
+      if (complain & tf_warning_or_error)
+	pedwarn (input_location, 0, "alias template deduction only available "
+		 "with %<-std=c++2a%> or %<-std=gnu++2a%>");
+    }
+
   if (init && TREE_TYPE (init) == ptype)
     /* Using the template parm as its own argument.  */
     return ptype;
@@ -28081,7 +28406,6 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
   tree type = TREE_TYPE (tmpl);
 
   bool try_list_ctor = false;
-  bool copy_init = false;
 
   releasing_vec rv_args = NULL;
   vec<tree,va_gc> *&args = *&rv_args;
@@ -28089,7 +28413,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
     args = make_tree_vector ();
   else if (BRACE_ENCLOSED_INITIALIZER_P (init))
     {
-      if (CONSTRUCTOR_NELTS (init) == 1)
+      try_list_ctor = TYPE_HAS_LIST_CTOR (type);
+      if (try_list_ctor && CONSTRUCTOR_NELTS (init) == 1)
 	{
 	  /* As an exception, the first phase in 16.3.1.7 (considering the
 	     initializer list as a single argument) is omitted if the
@@ -28097,34 +28422,30 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
 	     where U is a specialization of C or a class derived from a
 	     specialization of C.  */
 	  tree elt = CONSTRUCTOR_ELT (init, 0)->value;
-	  copy_init = is_spec_or_derived (TREE_TYPE (elt), tmpl);
+	  if (is_spec_or_derived (TREE_TYPE (elt), tmpl))
+	    try_list_ctor = false;
 	}
-      try_list_ctor = !copy_init && TYPE_HAS_LIST_CTOR (type);
       if (try_list_ctor || is_std_init_list (type))
 	args = make_tree_vector_single (init);
       else
 	args = make_tree_vector_from_ctor (init);
     }
+  else if (TREE_CODE (init) == TREE_LIST)
+    args = make_tree_vector_from_list (init);
   else
-    {
-      if (TREE_CODE (init) == TREE_LIST)
-	args = make_tree_vector_from_list (init);
-      else
-	args = make_tree_vector_single (init);
+    args = make_tree_vector_single (init);
 
-      if (args->length() == 1)
-	copy_init = is_spec_or_derived (TREE_TYPE ((*args)[0]), tmpl);
-    }
+  /* Do this now to avoid problems with erroneous args later on.  */
+  args = resolve_args (args, complain);
+  if (args == NULL)
+    return error_mark_node;
 
-  tree dname = dguide_name (tmpl);
-  tree cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname,
-				      /*type*/false, /*complain*/false,
-				      /*hidden*/false);
-  bool elided = false;
+  tree cands = deduction_guides_for (tmpl, complain);
   if (cands == error_mark_node)
-    cands = NULL_TREE;
+    return error_mark_node;
 
   /* Prune explicit deduction guides in copy-initialization context.  */
+  bool elided = false;
   if (flags & LOOKUP_ONLYCONVERTING)
     {
       for (lkp_iterator iter (cands); !elided && iter; ++iter)
@@ -28143,37 +28464,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
 	}
     }
 
-  tree outer_args = NULL_TREE;
-  if (DECL_CLASS_SCOPE_P (tmpl)
-      && CLASSTYPE_TEMPLATE_INSTANTIATION (DECL_CONTEXT (tmpl)))
-    {
-      outer_args = CLASSTYPE_TI_ARGS (DECL_CONTEXT (tmpl));
-      type = TREE_TYPE (most_general_template (tmpl));
-    }
-
-  bool saw_ctor = false;
-  // FIXME cache artificial deduction guides
-  for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter)
-    {
-      /* Skip inherited constructors.  */
-      if (iter.using_p ())
-	continue;
-
-      tree guide = build_deduction_guide (type, *iter, outer_args, complain);
-      if (guide == error_mark_node)
-	return error_mark_node;
-      if ((flags & LOOKUP_ONLYCONVERTING)
-	  && DECL_NONCONVERTING_P (STRIP_TEMPLATE (guide)))
-	elided = true;
-      else
-	cands = lookup_add (guide, cands);
-
-      saw_ctor = true;
-    }
-
-  if (!copy_init)
-    if (tree guide = maybe_aggr_guide (type, init))
-      cands = lookup_add (guide, cands);
+  if (tree guide = maybe_aggr_guide (tmpl, init, args))
+    cands = lookup_add (guide, cands);
 
   tree call = error_mark_node;
 
@@ -28202,28 +28494,6 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
 	}
     }
 
-  /* Maybe generate an implicit deduction guide.  */
-  if (call == error_mark_node && args->length () < 2)
-    {
-      tree gtype = NULL_TREE;
-
-      if (args->length () == 1)
-	/* Generate a copy guide.  */
-	gtype = build_reference_type (type);
-      else if (!saw_ctor)
-	/* Generate a default guide.  */
-	gtype = type;
-
-      if (gtype)
-	{
-	  tree guide = build_deduction_guide (type, gtype, outer_args,
-					      complain);
-	  if (guide == error_mark_node)
-	    return error_mark_node;
-	  cands = lookup_add (guide, cands);
-	}
-    }
-
   if (elided && !cands)
     {
       error ("cannot deduce template arguments for copy-initialization"
@@ -28245,7 +28515,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
       --cp_unevaluated_operand;
     }
 
-  if (call == error_mark_node && (complain & tf_warning_or_error))
+  if (call == error_mark_node
+      && (complain & tf_warning_or_error))
     {
       error ("class template argument deduction failed:");
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index f500ee61442..8817860b4d3 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2338,6 +2338,9 @@ lookup_mark (tree ovl, bool val)
 tree
 lookup_add (tree fns, tree lookup)
 {
+  if (fns == error_mark_node || lookup == error_mark_node)
+    return error_mark_node;
+
   if (lookup || TREE_CODE (fns) == TEMPLATE_DECL)
     {
       lookup = ovl_make (fns, lookup);
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C
index 513e16057af..e98573729f3 100644
--- a/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C
@@ -3,4 +3,4 @@
 
 template<class S> struct C;
 template<> struct C<int> { C(int, int) {} };
-auto k = C{0, 0};  // { dg-error "cannot deduce" }
+auto k = C{0, 0};  // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C
new file mode 100644
index 00000000000..ed47eb38f1b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C
@@ -0,0 +1,27 @@
+// Testcase from P1814R0
+// { dg-do compile { target c++2a } }
+
+template <class T> struct identity { using type = T; };
+template <class T> using identity_t = typename identity<T>::type;
+template <class T> concept Int = __is_same_as (T, int);
+
+template <class T, class U> struct C {
+  C(T, U);			// #1 { dg-message "constraint" }
+};
+template<class T, class U>
+C(T, U) -> C<T, identity_t<U>>; // #2 { dg-message "constraint" }
+
+template<class V>
+using A = C<V *, V *>;
+
+template<Int W>
+using B = A<W>;
+    
+int i{};
+double d{};
+A a1(&i, &i); // { dg-bogus "" "Deduces A<int>" }
+A a2(i, i);   // { dg-error "" "cannot deduce V * from i" }
+A a3(&i, &d); // { dg-error "" } #1: Cannot deduce (V*, V*) from (int *, double *) 
+                              // #2: Cannot deduce A<V> from C<int *, double *>
+B b1(&i, &i); // { dg-bogus "" "Deduces B<int>" }
+B b2(&d, &d); // { dg-error "" "cannot deduce B<W> from C<double *, double *>" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C
new file mode 100644
index 00000000000..d855f7d11ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C
@@ -0,0 +1,22 @@
+// Test that a non-template deduction guide that doesn't match the alias is
+// ignored.
+// { dg-do compile { target c++2a } }
+
+template <class T> struct identity { using type = T; };
+template <class T> using identity_t = typename identity<T>::type;
+
+template <class T, class U> struct C {
+  C(T, U);			// #1
+};
+
+C(char*, char*) -> C<int,int>;  // #3
+
+template<class V>
+using A = C<V *, V *>;
+
+char c;
+A a4 (&c, &c);			// ignores #3 because C<int,int> is not an A<V>
+
+static_assert (__is_same_as(decltype(a4),A<char>));
+
+C c2 (&c, &c);			// { dg-error "conversion" } deduces with #3
diff --git a/gcc/testsuite/g++.dg/cpp2a/explicit11.C b/gcc/testsuite/g++.dg/cpp2a/explicit11.C
index ad1bed5b3f0..2df42cdfec5 100644
--- a/gcc/testsuite/g++.dg/cpp2a/explicit11.C
+++ b/gcc/testsuite/g++.dg/cpp2a/explicit11.C
@@ -9,7 +9,7 @@ struct A {
 };
 
 int i;
-A a1 = { i, i }; // { dg-error "deduction|cannot" }
+A a1 = { i, i }; // { dg-error "deduction|cannot|no match" }
 A a2{ i, i };
 A a3{ 0, i };
 A a4 = { 0, i };
diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 753a6ecd0a8..9b6e2f59d2c 100644
--- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -358,8 +358,8 @@
 
 #ifndef __cpp_deduction_guides
 #  error "__cpp_deduction_guides"
-#elif __cpp_deduction_guides != 201703
-#  error "__cpp_deduction_guides != 201703"
+#elif __cpp_deduction_guides != 201907
+#  error "__cpp_deduction_guides != 201907"
 #endif
 
 #ifndef __cpp_if_constexpr

Reply via email to